Use newly added java-libglom API to create queries.
[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.io.File;
24 import java.io.FilenameFilter;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.sql.SQLException;
28 import java.util.ArrayList;
29 import java.util.Hashtable;
30 import java.util.Properties;
31
32 import javax.servlet.ServletException;
33
34 import org.glom.libglom.BakeryDocument.LoadFailureCodes;
35 import org.glom.libglom.Document;
36 import org.glom.libglom.Glom;
37 import org.glom.web.client.OnlineGlomService;
38 import org.glom.web.shared.DataItem;
39 import org.glom.web.shared.DetailsLayoutAndData;
40 import org.glom.web.shared.DocumentInfo;
41 import org.glom.web.shared.Documents;
42 import org.glom.web.shared.NavigationRecord;
43 import org.glom.web.shared.layout.LayoutGroup;
44
45 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
46 import com.mchange.v2.c3p0.DataSources;
47
48 /**
49  * The servlet class for setting up the server side of Online Glom. The public methods in this class are the methods
50  * that can be called by the client side code.
51  * 
52  * @author Ben Konrath <ben@bagu.org>
53  */
54 @SuppressWarnings("serial")
55 public class OnlineGlomServiceImpl extends RemoteServiceServlet implements OnlineGlomService {
56
57         private static final String GLOM_FILE_EXTENSION = ".glom";
58
59         // convenience class to for dealing with the Online Glom configuration file
60         private class OnlineGlomProperties extends Properties {
61                 public String getKey(String value) {
62                         for (String key : stringPropertyNames()) {
63                                 if (getProperty(key).trim().equals(value))
64                                         return key;
65                         }
66                         return null;
67                 }
68         }
69
70         private final Hashtable<String, ConfiguredDocument> documentMapping = new Hashtable<String, ConfiguredDocument>();
71
72         /*
73          * This is called when the servlet is started or restarted.
74          * 
75          * (non-Javadoc)
76          * 
77          * @see javax.servlet.GenericServlet#init()
78          */
79         @Override
80         public void init() throws ServletException {
81                 // Find the configuration file. See this thread for background info:
82                 // http://stackoverflow.com/questions/2161054/where-to-place-properties-files-in-a-jsp-servlet-web-application
83                 // FIXME move onlineglom.properties to the WEB-INF folder (option number 2 from the stackoverflow question)
84                 OnlineGlomProperties config = new OnlineGlomProperties();
85                 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("onlineglom.properties");
86                 if (is == null) {
87                         Log.fatal("onlineglom.properties not found.");
88                         throw new ServletException("onlineglom.properties not found.");
89                 }
90                 try {
91                         config.load(is);
92                 } catch (IOException e) {
93                         throw new ServletException(e.getMessage(), e);
94                 }
95
96                 // check if we can read the configured glom file directory
97                 String documentDirName = config.getProperty("glom.document.directory");
98                 File documentDir = new File(documentDirName);
99                 if (!documentDir.isDirectory()) {
100                         Log.fatal(documentDirName + " is not a directory.");
101                         throw new ServletException(documentDirName + " is not a directory.");
102                 }
103                 if (!documentDir.canRead()) {
104                         Log.fatal("Can't read the files in : " + documentDirName);
105                         throw new ServletException("Can't read the files in : " + documentDirName);
106                 }
107
108                 // get and check the glom files in the specified directory
109                 File[] glomFiles = documentDir.listFiles(new FilenameFilter() {
110                         @Override
111                         public boolean accept(File dir, String name) {
112                                 return name.endsWith(GLOM_FILE_EXTENSION);
113                         }
114                 });
115
116                 // don't continue if there aren't any Glom files to configure
117                 if (glomFiles.length <= 0) {
118                         Log.error("Unable to find any Glom documents in the configured directory: " + documentDirName);
119                         Log.error("Check the onlineglom.properties file to ensure that 'glom.document.directory' is set to the correct directory.");
120                         return;
121                 }
122
123                 Glom.libglom_init();
124                 for (File glomFile : glomFiles) {
125                         Document document = new Document();
126                         document.set_file_uri("file://" + glomFile.getAbsolutePath());
127                         int error = 0;
128                         boolean retval = document.load(error);
129                         if (retval == false) {
130                                 String message;
131                                 if (LoadFailureCodes.LOAD_FAILURE_CODE_NOT_FOUND == LoadFailureCodes.swigToEnum(error)) {
132                                         message = "Could not find file: " + glomFile.getAbsolutePath();
133                                 } else {
134                                         message = "An unknown error occurred when trying to load file: " + glomFile.getAbsolutePath();
135                                 }
136                                 Log.error(message);
137                                 // continue with for loop because there may be other documents in the directory
138                                 continue;
139                         }
140
141                         ConfiguredDocument configuredDocument;
142                         try {
143                                 configuredDocument = new ConfiguredDocument(document);
144                         } catch (PropertyVetoException e) {
145                                 throw new ServletException(e.getMessage(), e);
146                         }
147                         // check if a username and password have been set and work for the current document
148                         String filename = glomFile.getName();
149                         String key = config.getKey(filename);
150                         if (key != null) {
151                                 String[] keyArray = key.split("\\.");
152                                 if (keyArray.length == 3 && "filename".equals(keyArray[2])) {
153                                         // username/password could be set, let's check to see if it works
154                                         String usernameKey = key.replaceAll(keyArray[2], "username");
155                                         String passwordKey = key.replaceAll(keyArray[2], "password");
156                                         try {
157                                                 configuredDocument.setUsernameAndPassword(config.getProperty(usernameKey).trim(),
158                                                                 config.getProperty(passwordKey));
159                                         } catch (SQLException e) {
160                                                 throw new ServletException(e.getMessage(), e);
161                                         }
162                                 }
163                         }
164
165                         // check the if the global username and password have been set and work with this document
166                         if (!configuredDocument.isAuthenticated()) {
167                                 try {
168                                         configuredDocument.setUsernameAndPassword(config.getProperty("glom.document.username").trim(),
169                                                         config.getProperty("glom.document.password"));
170                                 } catch (SQLException e) {
171                                         throw new ServletException(e.getMessage(), e);
172                                 }
173                         }
174
175                         // The key for the hash table is the file name without the .glom extension and with spaces ( ) replaced with
176                         // pluses (+). The space/plus replacement makes the key more friendly for URLs.
177                         String documentID = filename.substring(0, glomFile.getName().length() - GLOM_FILE_EXTENSION.length())
178                                         .replace(' ', '+');
179                         configuredDocument.setDocumentID(documentID);
180                         documentMapping.put(documentID, configuredDocument);
181                 }
182
183                 // Allow a fake connection, so sqlbuilder_get_full_query() can work:
184                 Glom.set_fake_connection();
185         }
186
187         /*
188          * This is called when the servlet is stopped or restarted.
189          * 
190          * @see javax.servlet.GenericServlet#destroy()
191          */
192         @Override
193         public void destroy() {
194                 Glom.libglom_deinit();
195
196                 for (String documenTitle : documentMapping.keySet()) {
197                         ConfiguredDocument configuredDoc = documentMapping.get(documenTitle);
198                         try {
199                                 DataSources.destroy(configuredDoc.getCpds());
200                         } catch (SQLException e) {
201                                 Log.error(documenTitle, "Error cleaning up the ComboPooledDataSource.", e);
202                         }
203                 }
204         }
205
206         /*
207          * (non-Javadoc)
208          * 
209          * @see org.glom.web.client.OnlineGlomService#getDocumentInfo(java.lang.String)
210          */
211         @Override
212         public DocumentInfo getDocumentInfo(String documentID) {
213
214                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
215
216                 // FIXME check for authentication
217
218                 return configuredDoc.getDocumentInfo();
219
220         }
221
222         /*
223          * (non-Javadoc)
224          * 
225          * @see org.glom.web.client.OnlineGlomService#getListViewLayout(java.lang.String, java.lang.String)
226          */
227         @Override
228         public LayoutGroup getListViewLayout(String documentID, String tableName) {
229                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
230
231                 // FIXME check for authentication
232
233                 return configuredDoc.getListViewLayoutGroup(tableName);
234         }
235
236         /*
237          * (non-Javadoc)
238          * 
239          * @see org.glom.web.client.OnlineGlomService#getListViewData(java.lang.String, java.lang.String, int, int)
240          */
241         @Override
242         public ArrayList<DataItem[]> getListViewData(String documentID, String tableName, int start, int length) {
243                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
244                 if (!configuredDoc.isAuthenticated()) {
245                         return new ArrayList<DataItem[]>();
246                 }
247                 return configuredDoc.getListViewData(tableName, start, length, false, 0, false);
248         }
249
250         /*
251          * (non-Javadoc)
252          * 
253          * @see org.glom.web.client.OnlineGlomService#getSortedListViewData(java.lang.String, java.lang.String, int, int,
254          * int, boolean)
255          */
256         @Override
257         public ArrayList<DataItem[]> getSortedListViewData(String documentID, String tableName, int start, int length,
258                         int sortColumnIndex, boolean isAscending) {
259                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
260                 if (!configuredDoc.isAuthenticated()) {
261                         return new ArrayList<DataItem[]>();
262                 }
263                 return configuredDoc.getListViewData(tableName, start, length, true, sortColumnIndex, isAscending);
264         }
265
266         /*
267          * (non-Javadoc)
268          * 
269          * @see org.glom.web.client.OnlineGlomService#getDocuments()
270          */
271         @Override
272         public Documents getDocuments() {
273                 Documents documents = new Documents();
274                 for (String documentID : documentMapping.keySet()) {
275                         ConfiguredDocument configuredDoc = documentMapping.get(documentID);
276                         documents.addDocument(documentID, configuredDoc.getDocument().get_database_title());
277                 }
278                 return documents;
279         }
280
281         /*
282          * (non-Javadoc)
283          * 
284          * @see org.glom.web.client.OnlineGlomService#isAuthenticated(java.lang.String)
285          */
286         public boolean isAuthenticated(String documentID) {
287                 return documentMapping.get(documentID).isAuthenticated();
288         }
289
290         /*
291          * (non-Javadoc)
292          * 
293          * @see org.glom.web.client.OnlineGlomService#checkAuthentication(java.lang.String, java.lang.String,
294          * java.lang.String)
295          */
296         @Override
297         public boolean checkAuthentication(String documentID, String username, String password) {
298                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
299                 try {
300                         return configuredDoc.setUsernameAndPassword(username, password);
301                 } catch (SQLException e) {
302                         Log.error(documentID, "Unknown SQL Error checking the database authentication.", e);
303                         return false;
304                 }
305         }
306
307         /*
308          * (non-Javadoc)
309          * 
310          * @see org.glom.web.client.OnlineGlomService#getDetailsData(java.lang.String, java.lang.String, java.lang.String)
311          */
312         @Override
313         public DataItem[] getDetailsData(String documentID, String tableName, String primaryKeyValue) {
314                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
315
316                 // FIXME check for authentication
317
318                 return configuredDoc.getDetailsData(tableName, primaryKeyValue);
319         }
320
321         /*
322          * (non-Javadoc)
323          * 
324          * @see org.glom.web.client.OnlineGlomService#getDetailsLayoutAndData(java.lang.String, java.lang.String,
325          * java.lang.String)
326          */
327         @Override
328         public DetailsLayoutAndData getDetailsLayoutAndData(String documentID, String tableName, String primaryKeyValue) {
329                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
330                 DetailsLayoutAndData initalDetailsView = new DetailsLayoutAndData();
331
332                 // FIXME check for authentication
333
334                 initalDetailsView.setLayout(configuredDoc.getDetailsLayoutGroup(tableName));
335                 initalDetailsView.setData(configuredDoc.getDetailsData(tableName, primaryKeyValue));
336
337                 return initalDetailsView;
338         }
339
340         /*
341          * (non-Javadoc)
342          * 
343          * @see org.glom.web.client.OnlineGlomService#getRelatedListData(java.lang.String, java.lang.String, int, int)
344          */
345         @Override
346         public ArrayList<DataItem[]> getRelatedListData(String documentID, String tableName, String relationshipName,
347                         String foreignKeyValue, int start, int length) {
348                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
349
350                 // FIXME check for authentication
351
352                 return configuredDoc.getRelatedListData(tableName, relationshipName, foreignKeyValue, start, length, false, 0,
353                                 false);
354         }
355
356         /*
357          * (non-Javadoc)
358          * 
359          * @see org.glom.web.client.OnlineGlomService#getSortedRelatedListData(java.lang.String, java.lang.String, int, int,
360          * int, boolean)
361          */
362         @Override
363         public ArrayList<DataItem[]> getSortedRelatedListData(String documentID, String tableName, String relationshipName,
364                         String foreignKeyValue, int start, int length, int sortColumnIndex, boolean ascending) {
365                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
366
367                 // FIXME check for authentication
368
369                 return configuredDoc.getRelatedListData(tableName, relationshipName, foreignKeyValue, start, length, true,
370                                 sortColumnIndex, ascending);
371         }
372
373         public int getRelatedListRowCount(String documentID, String tableName, String relationshipName,
374                         String foreignKeyValue) {
375                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
376
377                 // FIXME check for authentication
378
379                 return configuredDoc.getRelatedListRowCount(tableName, relationshipName, foreignKeyValue);
380         }
381
382         /*
383          * (non-Javadoc)
384          * 
385          * @see org.glom.web.client.OnlineGlomService#getSuitableRecordToViewDetails(java.lang.String, java.lang.String,
386          * java.lang.String, java.lang.String)
387          */
388         @Override
389         public NavigationRecord getSuitableRecordToViewDetails(String documentID, String tableName,
390                         String relationshipName, String primaryKeyValue) {
391                 ConfiguredDocument configuredDoc = documentMapping.get(documentID);
392
393                 // FIXME check for authentication
394
395                 return configuredDoc.getSuitableRecordToViewDetails(tableName, relationshipName, primaryKeyValue);
396         }
397 }