Enable the CellTable Pager when more than 20 rows need to be viewed.
[online-glom:gwt-glom.git] / src / main / java / org / glom / web / server / OnlineGlomServiceImpl.java
1 /*
2  * Copyright (C) 2010, 2011 Openismus GmbH
3  *
4  * This file is part of GWT-Glom.
5  *
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.
10  *
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
14  * for more details.
15  *
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/>.
18  */
19
20 package org.glom.web.server;
21
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;
28
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;
43
44 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
45 import com.mchange.v2.c3p0.ComboPooledDataSource;
46 import com.mchange.v2.c3p0.DataSources;
47
48 @SuppressWarnings("serial")
49 public class OnlineGlomServiceImpl extends RemoteServiceServlet implements OnlineGlomService {
50         private Document document;
51         ComboPooledDataSource cpds;
52
53         // Called only when the servlet is stopped (the servlet container is stopped or restarted)
54         public OnlineGlomServiceImpl() {
55                 Glom.libglom_init();
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");
59                 int error = 0;
60                 @SuppressWarnings("unused")
61                 boolean retval = document.load(error);
62                 // TODO handle error condition (also below)
63
64                 cpds = new ComboPooledDataSource();
65                 // load the jdbc driver
66                 try {
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
70                         e.printStackTrace();
71                 }
72
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
76                 cpds.setUser("ben");
77                 cpds.setPassword("ChangeMe"); // of course it's not the password I'm using on my server
78         }
79
80         /*
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
83          */
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.");
87                 }
88                 return (int) l;
89         }
90
91         public GlomDocument getGlomDocument() {
92                 GlomDocument glomDocument = new GlomDocument();
93
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;
107                         }
108                         tableTitles[i] = document.get_table_title(tableName);
109                 }
110
111                 // set everything we need
112                 glomDocument.setTableNames(tableNames);
113                 glomDocument.setTableTitles(tableTitles);
114                 glomDocument.setTitle(document.get_database_title());
115
116                 return glomDocument;
117         }
118
119         public LayoutListTable getLayoutListTable(String tableName) {
120
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();
127                 }
128
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);
134                         if (field != null) {
135                                 layoutFields.add(field);
136                         }
137                 }
138
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
141                 // first page
142                 // TODO this code is really similar to code in getTableData, find a way to not duplicate the code
143                 int numRows;
144                 Connection conn = null;
145                 Statement st = null;
146                 ResultSet rs = null;
147                 try {
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);
153
154                         // get the number of rows in the query
155                         rs.setFetchDirection(ResultSet.FETCH_FORWARD);
156                         rs.last();
157                         numRows = rs.getRow();
158
159                 } catch (SQLException e) {
160                         // TODO log error
161                         // we don't know how many rows are in the query
162                         e.printStackTrace();
163                         numRows = 0;
164                 } finally {
165                         // this is a little awkward but we want to make we're cleaning everything up that has been used
166                         try {
167                                 if (rs != null)
168                                         rs.close();
169                                 if (st != null)
170                                         st.close();
171                                 if (conn != null)
172                                         conn.close();
173                         } catch (SQLException e) {
174                                 // TODO log error
175                                 e.printStackTrace();
176                         }
177                 }
178
179                 return new LayoutListTable(tableName, document.get_table_title(tableName), columnTitles, numRows);
180         }
181
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();
185
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);
192                         if (field != null) {
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
197                                 }
198                         }
199                 }
200
201                 ArrayList<String[]> rowsList = new ArrayList<String[]>();
202                 Connection conn = null;
203                 Statement st = null;
204                 ResultSet rs = null;
205                 try {
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);
211
212                         // get data we're asked for
213                         // TODO get the correct range with an sql query
214                         rs.setFetchDirection(ResultSet.FETCH_FORWARD);
215                         rs.absolute(start);
216                         int rowCount = 0;
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);
221                                 }
222                                 rowsList.add(rowArray);
223                                 rowCount++;
224                         }
225                 } catch (SQLException e) {
226                         // TODO: log error, notify user of problem
227                         e.printStackTrace();
228                 } finally {
229                         // this is a little awkward but we want to make we're cleaning everything up that has been used
230                         try {
231                                 if (rs != null)
232                                         rs.close();
233                                 if (st != null)
234                                         st.close();
235                                 if (conn != null)
236                                         conn.close();
237                         } catch (SQLException e) {
238                                 // TODO log error
239                                 e.printStackTrace();
240                         }
241                 }
242                 return rowsList;
243         }
244
245         // Called only when the servlet is stopped (the servlet container is stopped or restarted)
246         public void destroy() {
247                 Glom.libglom_deinit();
248                 try {
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
252                         e.printStackTrace();
253                 }
254         }
255
256 }