##### Coverage Report - org.melati.app.AbstractPoemApp

Classes in this File | Line Coverage | Branch Coverage | Complexity
AbstractPoemApp |
96% | 62/64
93% | 15/16
| 3.0;3
AbstractPoemApp$1 |
88% | 15/17
N/A | N/A
| 3.0;3

1 |  |
     /*
2 |  |
      * $Source: /usr/cvsroot/melati/melati/src/site/resources/withWebmacro/org.melati.app.AbstractPoemApp.html,v $
3 |  |
      * $Revision: 1.1 $
4 |  |
      *
5 |  |
      * Copyright (C) 2005 Tim Pizey
6 |  |
      *
7 |  |
      * Part of Melati (http://melati.org), a framework for the rapid
8 |  |
      * development of clean, maintainable web applications.
9 |  |
      *
10 |  |
      * Melati is free software; Permission is granted to copy, distribute
11 |  |
      * and/or modify this software under the terms either:
12 |  |
      *
13 |  |
      * a) the GNU General Public License as published by the Free Software
14 |  |
      *    Foundation; either version 2 of the License, or (at your option)
15 |  |
      *    any later version,
16 |  |
      *
17 |  |
      *    or
18 |  |
      *
19 |  |
      * b) any version of the Melati Software License, as published
20 |  |
      *    at http://melati.org
21 |  |
      *
22 |  |
      * You should have received a copy of the GNU General Public License and
23 |  |
      * the Melati Software License along with this program;
24 |  |
      * if not, write to the Free Software Foundation, Inc.,
25 |  |
      * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26 |  |
      * GNU General Public License and visit http://melati.org to obtain the
27 |  |
      * Melati Software License.
28 |  |
      *
29 |  |
      * Feel free to contact the Developers of Melati (http://melati.org),
30 |  |
      * if you would like to work out a different arrangement than the options
31 |  |
      * outlined here.  It is our intention to allow Melati to be used by as
32 |  |
      * wide an audience as possible.
33 |  |
      *
34 |  |
      * This program is distributed in the hope that it will be useful,
35 |  |
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 |  |
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37 |  |
      * GNU General Public License for more details.
38 |  |
      *
39 |  |
      * Contact details for copyright holder:
40 |  |
      *
41 |  |
      *     Tim Pizey <timp At paneris.org>
42 |  |
      *     http://paneris.org/~timp
43 |  |
      */
44 |  |
45 |  |
     package org.melati.app;
46 |  |
47 |  |
48 |  |
     import java.io.IOException;
49 |  |
50 |  |
     import org.melati.Melati;
51 |  |
     import org.melati.PoemContext;
52 |  |
     import org.melati.poem.AccessPoemException;
53 |  |
     import org.melati.poem.PoemDatabaseFactory;
54 |  |
     import org.melati.poem.PoemThread;
55 |  |
     import org.melati.poem.PoemTask;
56 |  |
     import org.melati.poem.AccessToken;
57 |  |
     import org.melati.poem.util.ArrayUtils;
58 |  |
     import org.melati.util.ConfigException;
59 |  |
     import org.melati.util.MelatiException;
60 |  |
     import org.melati.util.UnexpectedExceptionException;
61 |  |
62 |  |
     /**
63 |  |
      * Base class to use Poem as an application.
64 |  |
      *
65 |  |
      * <p>
66 |  |
      * Simply extend this class and override the {@link #doPoemRequest} method.
67 |  |
      * If you are going to use a template engine look at {@link AbstractTemplateApp}.
68 |  |
      * </p>
69 |  |
      *
70 |  |
      * <UL>
71 |  |
      * <LI>
72 |  |
      * The command line arguments are expected in the following order:
73 |  |
      * <BLOCKQUOTE><TT>
74 |  |
      * <BR>db
75 |  |
      * <BR>db method
76 |  |
      * <BR>db table method
77 |  |
      * <BR>db table troid  method
78 |  |
      * </TT></BLOCKQUOTE>
79 |  |
      *
80 |  |
      * these components are broken out of the command line arguments and passed to
81 |  |
      * your application code in the {@link Melati} parameter.
82 |  |
      *
83 |  |
      * <TABLE>
84 |  |
      *   <TR>
85 |  |
      *     <TD><TT><I>db</I></TT></TD>
86 |  |
      *     <TD>
87 |  |
      *       The first argument is taken to be the `logical name'
88 |  |
      *       of the POEM database to which the servlet should connect.  It
89 |  |
      *       is mapped onto JDBC connection details via the config file
90 |  |
      *       <TT>org.melati.LogicalDatabase.properties</TT>, of which there is an
91 |  |
      *       example in the source tree.  This is automatically made available in
92 |  |
      *       templates as <TT>$melati.Database</TT>.
93 |  |
      *     </TD>
94 |  |
      *   <TR>
95 |  |
      *     <TD><TT><I>table</I></TT></TD>
96 |  |
      *     <TD>
97 |  |
      *       The name of a table to work on:
98 |  |
      *       perhaps it is meant to list its contents.  This is automatically
99 |  |
      *       made available in templates as <TT>$melati.Table</TT>.
100 |  |
      *     </TD>
101 |  |
      *   </TR>
102 |  |
      *   <TR>
103 |  |
      *     <TD><TT><I>troid</I></TT></TD>
104 |  |
      *     <TD>
105 |  |
      *       The POEM `troid' (table row identifier, or row-unique integer) of a
106 |  |
      *       row within a <TT><I>table</I></TT>.
107 |  |
      *       This is automatically made
108 |  |
      *       available in templates as <TT>$melati.Object</TT>.
109 |  |
      *     </TD>
110 |  |
      *   </TR>
111 |  |
      *   <TR>
112 |  |
      *     <TD><TT><I>method</I></TT></TD>
113 |  |
      *     <TD>
114 |  |
      *       A freeform string telling your servlet what it is meant to do.  This
115 |  |
      *       is automatically made available in templates as
116 |  |
      *       <TT>$melati.Method</TT>.
117 |  |
      *     </TD>
118 |  |
      *   </TR>
119 |  |
      * </TABLE>
120 |  |
      *
121 |  |
      * <LI>
122 |  |
      * You can change the way these things are determined by overriding
123 |  |
      * {@link #poemContext}.
124 |  |
      *
125 |  |
      * <LI>
126 |  |
      * Any POEM database operations you perform will be done with the access
127 |  |
      * rights of the POEM <TT>User</TT> associated with the POEM session.  If
128 |  |
      * there is no established session, the current user will be set to
129 |  |
      * the default `guest' user.  If this method terminates with an
130 |  |
      * <TT>AccessPoemException</TT>, indicating that you have attempted something
131 |  |
      * which you aren't entitled to do, the user will be prompted to log in, and
132 |  |
      * the original request will be retried.  The precise mechanism used for
133 |  |
      * login is <A HREF=#loginmechanism>configurable</A>.
134 |  |
      *
135 |  |
      * <LI>
136 |  |
      * No changes made to the database by other concurrently executing threads
137 |  |
      * will be visible to you (in the sense that once you have seen a particular
138 |  |
      * version of a record, you will always subsequently see the same one), and
139 |  |
      * your own changes will not be made permanent until this method completes
140 |  |
      * successfully or you perform an explicit <TT>PoemThread.commit()</TT>.  If
141 |  |
      * it terminates with an exception or you issue a
142 |  |
      * <TT>PoemThread.rollback()</TT>, your changes will be lost.
143 |  |
      *
144 |  |
      * <LI>
145 |  |
      * <A NAME=loginmechanism></A>It is possible to configure how your
146 |  |
      * <TT>PoemApp</TT>-derived applications implement user login. If the
147 |  |
      * properties file <TT>org.melati.MelatiApp.properties</TT>
148 |  |
      * exists and contains a setting
149 |  |
      * <TT>org.melati.MelatiApp.accessHandler=<I>foo</I></TT>, then
150 |  |
      * <TT><I>foo</I></TT> is taken to be the name of a class implementing the
151 |  |
      * <TT>AccessHandler</TT> interface.
152 |  |
      * </UL>
153 |  |
      *
154 |  |
      * If you do not need access handling then set your accessHandler to
155 |  |
      * <tt>org.melati.login.OpenAccessHandler</tt>.
156 |  |
      * If you do need access handling then set your accessHandler to
157 |  |
      * <tt>org.melati.login.CommandLineAccessHandler</tt>.
158 |  |
      * However this is not extremely secure, as the user could potentially
159 |  |
      * change this seting to <tt>OpenAccessHandler</tt> as they are on the same machine.
160 |  |
      *
161 |  |
      * You can specify the username and password to use by adding command line parameters:
162 |  |
      * <pre>
163 |  |
      * <tt>-username user -password password</tt>
164 |  |
      * </pre>
165 |  |
      *
166 |  |
      *
167 |  |
      * @see org.melati.poem.Database#guestAccessToken
168 |  |
      * @see org.melati.poem.PoemThread#commit
169 |  |
      * @see org.melati.poem.PoemThread#rollback
170 |  |
      * @see #poemContext
171 |  |
      * @see org.melati.login.AccessHandler
172 |  |
      * @see org.melati.login.HttpSessionAccessHandler
173 |  |
      * @see org.melati.login.Login
174 |  |
      * @see org.melati.login.OpenAccessHandler
175 |  |
      * @see org.melati.login.CommandLineAccessHandler
176 |  |
      */
177 |  |
178 | 100 |
     public abstract class AbstractPoemApp extends AbstractConfigApp implements  App {
179 |  |
180 | 2 |
       private static Boolean taskPerformedOrLoggedInAndTaskAttempted = Boolean.FALSE;
181 |  |
182 |  |
       /**
183 |  |
        * Initialise.
184 |  |
        *
185 |  |
        * @param args the command line arguments
186 |  |
        * @return a configured Melati
187 |  |
        * {@inheritDoc}
188 |  |
        * @see org.melati.app.AbstractConfigApp#init(java.lang.String[])
189 |  |
        */
190 |  |
       public Melati init(String[] args)  throws MelatiException {
191 | 56 |
         Melati m = super.init(args);
192 | 54 |
         if (m.getDatabase() == null) {
193 |  |
           try {
194 | 2 |
             super.term(m);
195 | 0 |
           } catch (IOException e) {
196 | 0 |
             e = null;
197 | 2 |
           }
198 | 2 |
           throw new ConfigException("No database configured");
199 |  |
         }
200 | 52 |
         return m;
201 |  |
       }
202 |  |
203 |  |
       /**
204 |  |
        * Clean up at end of run.
205 |  |
        *
206 |  |
        * @param melati the melati
207 |  |
        * @throws IOException
208 |  |
        */
209 |  |
       public void term(Melati melati) throws IOException {
210 | 56 |
         super.term(melati);
211 | 56 |
         if (melati.getDatabase() != null)
212 | 56 |
           PoemDatabaseFactory.disconnectDatabase(melati.getDatabase().getName());
213 | 56 |
       }
214 |  |
215 |  |
       /**
216 |  |
        * A place holder for things you might want to do before
217 |  |
        * setting up a <code>PoemSession</code>.
218 |  |
        *
219 |  |
        * @param melati the current Melati
220 |  |
        * @throws Exception if anything goes wrong
221 |  |
        */
222 |  |
       protected void prePoemSession(Melati melati) throws Exception {
223 | 44 |
         Melati foolEclipse = melati;
224 | 44 |
         melati = foolEclipse;
225 | 44 |
       }
226 |  |
227 |  |
       protected void doConfiguredRequest(final Melati melati) {
228 |  |
         // Do something outside of the PoemSession
229 |  |
         try {
230 | 46 |
           melati.getConfig().getAccessHandler().buildRequest(melati);
231 | 46 |
           prePoemSession(melati);
232 | 2 |
         } catch (Exception e) {
233 | 2 |
           throw new UnexpectedExceptionException(e);
234 | 44 |
         }
235 |  |
236 |  |
         // Login loop
237 |  |
         // If not logged-in when required then an exception is thrown.
238 |  |
         // The exception is handled and the task revisited
239 |  |
         // The flag is reset to allow this to be run again.
240 | 44 |
         synchronized(taskPerformedOrLoggedInAndTaskAttempted) {
241 | 44 |
           taskPerformedOrLoggedInAndTaskAttempted = Boolean.FALSE;
242 |  |
           //int goes = 0;
243 | 84 |
           while (taskPerformedOrLoggedInAndTaskAttempted.equals(Boolean.FALSE)) {
244 |  |
             //goes ++;
245 |  |
             //if (goes > 2)
246 |  |
             //  throw new MelatiBugMelatiException("Problem with login loop logic, goes = " + goes);
247 | 48 |
             melati.getDatabase().inSession (
248 | 48 |
               AccessToken.root, new PoemTask() {
249 |  |
                 public void run () {
250 | 48 |
                   melati.getConfig().getAccessHandler().establishUser(melati);
251 | 48 |
                   melati.loadTableAndObject();
252 |  |
                   try {
253 |  |
                     try {
254 | 48 |
                       doPoemRequest(melati);
255 | 36 |
                       taskPerformedOrLoggedInAndTaskAttempted = Boolean.TRUE;
256 | 12 |
                     } catch (Exception e) {
257 | 12 |
                       _handleException (melati, e);
258 | 36 |
                     }
259 | 8 |
                   } catch (Exception e) {
260 | 8 |
                     taskPerformedOrLoggedInAndTaskAttempted = Boolean.TRUE;
261 |  |
                     try {
262 | 8 |
                       term(melati);
263 | 0 |
                     } catch (IOException e1) {
264 | 0 |
                       e1 = null;
265 | 8 |
                     }
266 | 8 |
                     throw new UnhandledExceptionException(e);
267 | 40 |
                   }
268 | 40 |
                 }
269 |  |
270 |  |
                 // Not sure there is any point in this
271 |  |
                 // Cannot find a way of accessing it
272 |  |
                 //public String toString() {
273 |  |
                 //  return "PoemApp";
274 |  |
                 //}
275 |  |
               }
276 |  |
             );
277 |  |
           }
278 | 36 |
         }
279 | 36 |
       }
280 |  |
281 |  |
      /**
282 |  |
       * Default method to handle an exception.
283 |  |
       *
284 |  |
       * @param melati the Melati
285 |  |
       * @param exception the exception to handle
286 |  |
       */
287 |  |
       protected static void handleException(Melati melati, Exception exception)
288 |  |
           throws Exception {
289 |  |
290 | 12 |
         if (exception instanceof AccessPoemException) {
291 | 10 |
           melati.getConfig().getAccessHandler()
292 |  |
             .handleAccessException(melati,(AccessPoemException)exception);
293 |  |
294 |  |
         }
295 |  |
         else
296 | 2 |
           throw exception;
297 | 4 |
       }
298 |  |
299 |  |
       protected final void _handleException(Melati melati, Exception exception)
300 |  |
            throws Exception {
301 |  |
         try {
302 | 12 |
           handleException(melati, exception);
303 |  |
         }
304 | 8 |
         catch (Exception e) {
305 | 8 |
           PoemThread.rollback();
306 | 8 |
           throw e;
307 | 4 |
         }
308 | 4 |
       }
309 |  |
310 |  |
311 |  |
       protected PoemContext poemContext(Melati melati)
312 |  |
           throws InvalidArgumentsException {
313 | 38 |
         String[] args = melati.getArguments();
314 |  |
315 | 38 |
         PoemContext pc = new PoemContext();
316 | 38 |
         if (args.length > 0) {
317 | 36 |
           pc.setLogicalDatabase(args[0]);
318 | 36 |
           setTableTroidMethod(pc, (String[])ArrayUtils.section(args,  1,  args.length));
319 |  |
         }
320 |  |
321 | 36 |
         return pc;
322 |  |
       }
323 |  |
324 |  |
       protected void setTableTroidMethod(PoemContext pc, String[] args){
325 | 54 |
         if (args.length == 1) {
326 | 6 |
           pc.setMethod(args[0]);
327 |  |
         }
328 | 54 |
         if (args.length == 2) {
329 | 6 |
           pc.setTable(args[0]);
330 |  |
           try {
331 | 6 |
             pc.setTroid(new Integer (args[1]));
332 |  |
           }
333 | 2 |
           catch (NumberFormatException e) {
334 | 2 |
             pc.setMethod(args[1]);
335 | 4 |
           }
336 |  |
         }
337 | 54 |
         if (args.length >= 3) {
338 | 36 |
           pc.setTable(args[0]);
339 |  |
           try {
340 | 36 |
             pc.setTroid(new Integer (args[1]));
341 |  |
           }
342 | 2 |
           catch (NumberFormatException e) {
343 | 2 |
             throw new UnexpectedExceptionException(new InvalidArgumentsException(args, e));
344 | 34 |
           }
345 | 34 |
           pc.setMethod(args[2]);
346 |  |
         }
347 | 52 |
       }
348 |  |
349 |  |
350 |  |
       /**
351 |  |
        * This is provided for convenience, so you don't have to specify the
352 |  |
        * logical database in the arguments.  This is useful when
353 |  |
        * writing applications where you are only accessing a single database.
354 |  |
        *
355 |  |
        * Simply override {@link #poemContext(Melati melati)} thus:
356 |  |
        *
357 |  |
        * <PRE>
358 |  |
        * protected PoemContext poemContext(Melati melati)
359 |  |
        *     throws InvalidArgumentsException {
360 |  |
        *   return poemContextWithLDB(melati,"<your logical database name>");
361 |  |
        * }
362 |  |
        * </PRE>
363 |  |
        *
364 |  |
        */
365 |  |
       protected PoemContext poemContextWithLDB(Melati melati,
366 |  |
                                                String logicalDatabase)
367 |  |
           throws InvalidArgumentsException {
368 | 18 |
         PoemContext pc = new PoemContext();
369 | 18 |
         pc.setLogicalDatabase(logicalDatabase);
370 | 18 |
         setTableTroidMethod(pc, melati.getArguments());
371 | 18 |
         return pc;
372 |  |
       }
373 |  |
374 |  |
       /**
375 |  |
        * Override this method to do your own thing.
376 |  |
        *
377 |  |
        * @param melati a {@link Melati} containing POEM and other configuration data
378 |  |
        */
379 |  |
       protected abstract void doPoemRequest(Melati melati) throws Exception;
380 |  |
381 |  |
     }