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