2 * Copyright (C) 2010, 2011 Openismus GmbH
4 * This file is part of GWT-Glom.
6 * GWT-Glom is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
11 * GWT-Glom is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with GWT-Glom. If not, see <http://www.gnu.org/licenses/>.
20 package org.glom.web.server;
22 import java.beans.PropertyVetoException;
23 import java.sql.Connection;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.util.ArrayList;
29 import org.glom.libglom.Document;
30 import org.glom.libglom.Field;
31 import org.glom.libglom.Glom;
32 import org.glom.libglom.LayoutFieldVector;
33 import org.glom.libglom.LayoutGroupVector;
34 import org.glom.libglom.LayoutItem;
35 import org.glom.libglom.LayoutItemVector;
36 import org.glom.libglom.LayoutItem_Field;
37 import org.glom.libglom.SortClause;
38 import org.glom.libglom.SortFieldPair;
39 import org.glom.libglom.StringVector;
40 import org.glom.web.client.OnlineGlomService;
41 import org.glom.web.shared.GlomDocument;
42 import org.glom.web.shared.LayoutListTable;
44 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
45 import com.mchange.v2.c3p0.ComboPooledDataSource;
46 import com.mchange.v2.c3p0.DataSources;
48 @SuppressWarnings("serial")
49 public class OnlineGlomServiceImpl extends RemoteServiceServlet implements OnlineGlomService {
50 private Document document;
51 ComboPooledDataSource cpds;
53 // Called only when the servlet is stopped (the servlet container is stopped or restarted)
54 public OnlineGlomServiceImpl() {
56 document = new Document();
57 // TODO hardcoded for now, need to figure out something for this
58 document.set_file_uri("file:///home/ben/music-collection.glom");
60 @SuppressWarnings("unused")
61 boolean retval = document.load(error);
62 // TODO handle error condition (also below)
64 cpds = new ComboPooledDataSource();
65 // load the jdbc driver
67 cpds.setDriverClass("org.postgresql.Driver");
68 } catch (PropertyVetoException e) {
69 // TODO log error, fatal error can't continue, user can be nofified when db access doesn't work
73 cpds.setJdbcUrl("jdbc:postgresql://" + document.get_connection_server() + "/"
74 + document.get_connection_database());
75 // TODO figure out something for db user name and password
77 cpds.setPassword("ChangeMe"); // of course it's not the password I'm using on my server
81 * FIXME I think Swig is generating long on 64-bit machines and int on 32-bit machines - need to keep this constant
82 * http://stackoverflow.com/questions/1590831/safely-casting-long-to-int-in-java
84 public static int safeLongToInt(long l) {
85 if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
86 throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
91 public GlomDocument getGlomDocument() {
92 GlomDocument glomDocument = new GlomDocument();
94 // get arrays of table names and titles, and find the default table index
95 StringVector tablesVec = document.get_table_names();
96 int numTables = safeLongToInt(tablesVec.size());
97 String[] tableNames = new String[numTables];
98 String[] tableTitles = new String[numTables];
99 boolean foundDefaultTable = false;
100 for (int i = 0; i < numTables; i++) {
101 String tableName = tablesVec.get(i);
102 tableNames[i] = tableName;
103 // JNI is "expensive", the comparison will only be called if we haven't already found the default table
104 if (!foundDefaultTable && tableName.equals(document.get_default_table())) {
105 glomDocument.setDefaultTableIndex(i);
106 foundDefaultTable = true;
108 tableTitles[i] = document.get_table_title(tableName);
111 // set everything we need
112 glomDocument.setTableNames(tableNames);
113 glomDocument.setTableTitles(tableTitles);
114 glomDocument.setTitle(document.get_database_title());
119 public LayoutListTable getLayoutListTable(String tableName) {
121 LayoutGroupVector layoutListVec = document.get_data_layout_groups("list", tableName);
122 LayoutItemVector layoutItemsVec = layoutListVec.get(0).get_items();
123 int numItems = safeLongToInt(layoutItemsVec.size());
124 String[] columnTitles = new String[numItems];
125 for (int i = 0; i < numItems; i++) {
126 columnTitles[i] = layoutItemsVec.get(i).get_title_or_name();
129 LayoutFieldVector layoutFields = new LayoutFieldVector();
130 for (int i = 0; i < numItems; i++) {
131 LayoutItem item = layoutItemsVec.get(i);
132 columnTitles[i] = item.get_title_or_name();
133 LayoutItem_Field field = LayoutItem_Field.cast_dynamic(item);
135 layoutFields.add(field);
139 // get the size of the returned query for the pager
140 // TODO since we're executing a query anyway, maybe we should return the rows that will be displayed on the
142 // TODO this code is really similar to code in getTableData, find a way to not duplicate the code
144 Connection conn = null;
148 // setup and execute the query
149 conn = cpds.getConnection();
150 st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
151 String query = Glom.build_sql_select_simple(tableName, layoutFields);
152 rs = st.executeQuery(query);
154 // get the number of rows in the query
155 rs.setFetchDirection(ResultSet.FETCH_FORWARD);
157 numRows = rs.getRow();
159 } catch (SQLException e) {
161 // we don't know how many rows are in the query
165 // this is a little awkward but we want to make we're cleaning everything up that has been used
173 } catch (SQLException e) {
179 return new LayoutListTable(tableName, document.get_table_title(tableName), columnTitles, numRows);
182 public ArrayList<String[]> getTableData(int start, int length, String table) {
183 LayoutGroupVector layoutList = document.get_data_layout_groups("list", table);
184 LayoutItemVector layoutItems = layoutList.get(0).get_items();
186 LayoutFieldVector layoutFields = new LayoutFieldVector();
187 SortClause sortClause = new SortClause();
188 int numItems = safeLongToInt(layoutItems.size());
189 for (int i = 0; i < numItems; i++) {
190 LayoutItem item = layoutItems.get(i);
191 LayoutItem_Field field = LayoutItem_Field.cast_dynamic(item);
193 layoutFields.add(field);
194 Field details = field.get_full_field_details();
195 if (details != null && details.get_primary_key()) {
196 sortClause.addLast(new SortFieldPair(field, true)); // ascending
201 ArrayList<String[]> rowsList = new ArrayList<String[]>();
202 Connection conn = null;
206 // setup and execute the query
207 conn = cpds.getConnection();
208 st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
209 String query = Glom.build_sql_select_simple(table, layoutFields, sortClause);
210 rs = st.executeQuery(query);
212 // get data we're asked for
213 // TODO get the correct range with an sql query
214 rs.setFetchDirection(ResultSet.FETCH_FORWARD);
217 while (rs.next() && rowCount <= length) {
218 String[] rowArray = new String[safeLongToInt(layoutItems.size())];
219 for (int i = 0; i < layoutItems.size(); i++) {
220 rowArray[i] = rs.getString(i + 1);
222 rowsList.add(rowArray);
225 } catch (SQLException e) {
226 // TODO: log error, notify user of problem
229 // this is a little awkward but we want to make we're cleaning everything up that has been used
237 } catch (SQLException e) {
245 // Called only when the servlet is stopped (the servlet container is stopped or restarted)
246 public void destroy() {
247 Glom.libglom_deinit();
249 DataSources.destroy(cpds);
250 } catch (SQLException e) {
251 // TODO log error, don't need to notify user because this is a clean up method