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