View Javadoc

1   /**
2    * Copyright 2005-2007 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.control.action;
16  
17  import org.figure8.join.util.LogUtil;
18  import org.figure8.join.control.JoinAction;
19  import org.figure8.join.control.form.SetupForm;
20  import org.figure8.join.core.InfrastructureException;
21  import org.figure8.join.core.DuplicateEntityException;
22  import org.figure8.join.core.setup.BootstrapUtil;
23  import org.figure8.join.core.setup.DatabaseDetails;
24  import org.figure8.join.core.setup.BootstrapManager;
25  import org.figure8.join.core.setup.ApplicationConfig;
26  import org.figure8.join.core.persistence.ObjectDao;
27  import org.figure8.join.core.persistence.XmlDatabinder;
28  import org.figure8.join.businessobjects.security.Role;
29  import org.figure8.join.businessobjects.security.User;
30  import org.figure8.join.businessobjects.security.Permission;
31  import org.figure8.join.businessfacades.security.UserManager;
32  import org.figure8.join.businessfacades.security.PermissionManager;
33  import org.figure8.join.view.UserView;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.struts.action.ActionForm;
37  import org.apache.struts.action.ActionForward;
38  import org.apache.struts.action.ActionMapping;
39  import org.apache.struts.action.ActionErrors;
40  import org.apache.struts.action.ActionMessage;
41  import org.apache.struts.Globals;
42  
43  import javax.servlet.http.HttpServletRequest;
44  import javax.servlet.http.HttpServletResponse;
45  
46  import java.io.InputStream;
47  import java.io.IOException;
48  import java.util.Collection;
49  import java.util.Iterator;
50  /**
51   * Struts action used for setting up Join application
52   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
53   * @author <a href="mailto:vincent.huynen@gmail.com">Vincent Huynen</a>
54   * @version $Revision: 1.3 $
55   *
56   * @struts.action path="/setup" name="setupForm"
57   *                scope="request" parameter="op" validate="true"
58   *                input="/pages/validationfailurepage.jsp"
59   * @struts.action-forward name="Setup" path="/jsp/setup/setup.jsp"
60   * @struts.action-forward name="Custom" path="/jsp/setup/setupcustom.jsp"
61   * @struts.action-forward name="Standard" path="/jsp/setup/setupstandard.jsp"
62   * @struts.action-forward name="Dissociated" path="/jsp/setup/setupsynchro.jsp"
63   * @struts.action-forward name="OtherSideAccess" path="/jsp/setup/setupotherside.jsp"
64   * @struts.action-forward name="DatabaseChoice" path="/jsp/setup/setupdatabasechoice.jsp"
65   * @struts.action-forward name="Database" path="/jsp/setup/setupdatabase.jsp"
66   * @struts.action-forward name="CustomAdmin" path="/jsp/setup/setupcustomadmin.jsp"
67   * @struts.action-forward name="Failure" path="/jsp/servicefailure.jsp"
68   */
69  public class SetupActions extends JoinAction{
70   
71     // Static -------------------------------------------------------------------
72     
73     /** Get a commons logger. */
74     private static Log log = LogUtil.getLog(SetupActions.class);
75  
76     /** Operation code for setting setup type */
77     public static final String SETUP_TYPE_OP = "setType";
78     /** Operation code for setting simple or dissociated installation */
79     public static final String SETUP_MODE_OP = "setupMode";
80     /** Operation code for setting synchronous property of installation */
81     public static final String SETUP_SYNCH_OP = "setupSynch";
82     /** Operation code for setting application other side properties */
83     public static final String SETUP_OTHERSIDE_OP = "setupOtherSide";
84     /** Operation code for setting administrator */
85     public static final String SETUP_ADMIN_OP = "setupAdmin";
86     /** Operation code for setting embedded database */
87     public static final String SETUP_EMBED_OP = "setupEmbedded";
88     /** Operation code for setting up external database */
89     public static final String SETUP_EXTERN_OP = "setupExternal";
90     /** Operation code for setting database details */
91     public static final String SETUP_DATAB_OP = "setupDatabase";
92     /** Operation code for setting datasource details */
93     public static final String SETUP_DATAS_OP = "setupDatasource";
94     /** Operation code for finishing setup process */
95     public static final String SETUP_FINISH_OP = "finish";
96  
97  
98     // Attributes ---------------------------------------------------------------
99  
100    /** Choosen setup type (first step of setup) */
101    protected String setupType;
102    /** Application bootstrap manager. */
103    protected BootstrapManager bootstrapManager;
104    /** Application configuration manager. */
105    protected ApplicationConfig appConfiguration;
106    /** Generic object dao for saving default entities */
107    protected ObjectDao genericDao;
108    /** Xml databinder for importing default entities from Xml */
109    protected XmlDatabinder dataBinder;
110    /** Join application user manager. */
111    protected UserManager userManager;
112    /** Join security permissions manager. */
113    protected PermissionManager permissionManager;
114 
115 
116    // Constructors -------------------------------------------------------------
117    
118    /** Creates a new instance of SetupActions */
119    public SetupActions(){
120    }
121    
122    
123    // Public -------------------------------------------------------------------
124    
125    /** @param manager <code>BoostrapManager</code> instance */
126    public void setBootstrapManager(BootstrapManager manager){
127       this.bootstrapManager = manager;
128    }
129    /** @param config <code>ApplicationConfig</code> instance */
130    public void setApplicationConfig(ApplicationConfig config){
131       this.appConfiguration = config;
132    }
133    /** @param genericDao <code>ObjectDao</code> implementation instance */
134    public void setObjectDao(ObjectDao genericDao){
135       this.genericDao = genericDao;
136    }
137    /** @param dataBinder <code>XmlDatabinder</code> implementation instance */
138    public void setXmlDatabinder(XmlDatabinder dataBinder){
139       this.dataBinder = dataBinder;
140    }
141    /** @param manager <code>UserManager</code> implementation instance */
142    public void setUserManager(UserManager manager){
143       this.userManager = manager;
144    }
145    /** @param manager <code>PermissionManager</code> implementation instance */
146    public void setPermissionManager(PermissionManager manager){
147       this.permissionManager = manager;
148    }
149 
150 
151    // Override of JoinAction ---------------------------------------------------
152    
153    /**
154     * Execute the requested operation within action.s
155     * @param operation String representing the operation to invoke on Action
156     * @param mapping Mapping between forwards name and path for this action
157     * @param form The form object containing request parameters
158     * @param request The servlet container request wrapper
159     * @param response The servlet container response wrapper
160     * @return A forward to the next view to render and display
161     * @throws Exception is something wrong occurs
162     */
163    public ActionForward doExecute(String operation, ActionMapping mapping, ActionForm form,
164                                   HttpServletRequest request, HttpServletResponse response) throws Exception{
165       // Trace operation to execute.
166       log.debug("doExecute() called for '" + operation + "' operation");
167 
168       if (SETUP_TYPE_OP.equals(operation)){
169          return setupType(mapping, form, request, response);
170       }
171       else if (SETUP_MODE_OP.equals(operation)){
172          return setupDissociation(mapping, form, request, response);
173       }
174       else if (SETUP_SYNCH_OP.equals(operation)){
175          return setupSynchronisation(mapping, form, request, response);
176       }
177       else if (SETUP_OTHERSIDE_OP.equals(operation)){
178          return setupOtherSideAccess(mapping, form, request, response);
179       }
180       else if (SETUP_ADMIN_OP.equals(operation)){
181          return setupAdministrator(mapping, form, request, response);
182       }
183       else if (SETUP_EMBED_OP.equals(operation)){
184          return setupEmbedded(mapping, form, request, response);
185       }
186       else if (SETUP_EXTERN_OP.equals(operation)){
187          return mapping.findForward("Database");
188       }
189       else if (SETUP_DATAB_OP.equals(operation)){
190          return setupDatabase(mapping, form, request, response);
191       }
192       else if (SETUP_DATAS_OP.equals(operation)){
193          return setupDatasource(mapping, form, request, response);
194       }
195       else if (SETUP_FINISH_OP.equals(operation)){
196          return finishSetup(mapping, form, request, response);
197       }
198       // This should not happen...
199       return null;
200    }
201    
202    
203    // Protected ----------------------------------------------------------------
204 
205    /**
206     * Save choosen setup type and forward according to this type.
207     * @return A forward to the next view to render and display
208     */
209    protected ActionForward setupType(ActionMapping mapping, ActionForm form,
210                                      HttpServletRequest request, HttpServletResponse response)
211            throws Exception{
212       if (form instanceof SetupForm){
213          // Retrieve BootstrapManager from util class.
214          // This is necessary because Spring services are not already launched !
215          setBootstrapManager(BootstrapUtil.getBootstrapManager());
216 
217          // Save setup type into application properties.
218          setupType = ((SetupForm)form).getSetupType();
219          bootstrapManager.setProperty(ApplicationConfig.SETUP_TYPE, setupType);
220          log.info("Current setup type is now: '" + setupType + "'");
221 
222          // Before going further, ensure we have a db in standard mode.
223          // Ensure also that we bootstrap the embedded messaging system.
224          if (isStandardSetup()){
225             log.info("Setup is standard. Bootstrap message broker and embedded database before registering admin...");
226             bootstrapManager.bootstrapMessaging(null, true);
227             log.info("Embedded messaging system has been bootstrapped.");
228             DatabaseDetails details = DatabaseDetails.getDefault("hsqldb");
229             bootstrapManager.bootstrapDatabase(details, true);
230             log.info("Embedded database has been bootstrapped.");
231          }
232 
233          return forward(mapping);
234       }
235       // This should not happen...
236       return null;
237    }
238 
239    /**
240     * Save the installation dissociation mode : the setup is whether simple or dissociated
241     * applications sides (one for synchronous services, one for asynchronous services)
242     * @return A forward to the next view to render and display
243     */
244    protected ActionForward setupDissociation(ActionMapping mapping, ActionForm form, HttpServletRequest request,
245                                              HttpServletResponse response) throws Exception{
246       if (form instanceof SetupForm){
247          SetupForm sForm = (SetupForm)form;
248          String isDissociated = ("Dissociated".equals(sForm.getSetupMode()) ? "true" : "false");
249          // Record result into boostrap manager.
250          bootstrapManager.setProperty(ApplicationConfig.DISSOCIATED, isDissociated);
251 
252          if (bootstrapManager.isDissociatedSetup()){
253             // We now have to choose between synchronous or asynchronous side.
254             log.info("Setup is custom & dissociated. Choose now the side of installation...");
255             return mapping.findForward("Dissociated");
256          }
257          else{
258             // We can now bootstrap the embedded messaging system.
259             log.info("Setup is custom & simple mode. Bootstrap message broker before registering database and admin.");
260             bootstrapManager.bootstrapMessaging(null, true);
261             return mapping.findForward("DatabaseChoice");
262          }
263       }
264       // This shoud not happen...
265       return null;
266    }
267 
268    /**
269     * Save configuration of synchronous or asynchronous side.
270     * @return A forward to the next view to render and display
271     */
272    protected ActionForward setupSynchronisation(ActionMapping mapping, ActionForm form, HttpServletRequest request,
273                                                 HttpServletResponse response) throws Exception{
274       if (form instanceof SetupForm){
275          SetupForm sForm = (SetupForm)form;
276          String isSynchronousSide = ("Synchronous".equals(sForm.getSetupSynch()) ? "true" : "false");
277          // Record result into boostrap manager.
278          bootstrapManager.setProperty(ApplicationConfig.SYNCH_SIDE, isSynchronousSide);
279          // Forward to the page for configuring access to over side.
280          return mapping.findForward("OtherSideAccess");
281       }
282       // This should not happen...
283       return null;
284    }
285 
286    /**
287     * Save configuration on how to access application other side
288     * @return A forward to the next view to render and display
289    */
290   protected ActionForward setupOtherSideAccess(ActionMapping mapping, ActionForm form, HttpServletRequest request,
291                                               HttpServletResponse response) throws Exception{
292       if (form instanceof SetupForm){
293          SetupForm sForm = (SetupForm)form;
294          // Depending on setup mode, bootstrap message with different options.
295          if (!bootstrapManager.isAsynchronousSide()){
296             // We can now bootstrap the messaging connection factory.
297             log.info("Setup is Custom, Dissociated & Synchronous. Bootstrap message broker connection...");
298             bootstrapManager.bootstrapMessaging(sForm.getBrokerUrl(), false);
299             bootstrapManager.setProperty(ApplicationConfig.OTHER_SIDE_URL, sForm.getHttpUrl());
300             return mapping.findForward("DatabaseChoice");
301          }
302          else{
303             // Compose a broker url using the port.
304             String brokerUrl = "tcp://localhost:" + sForm.getBrokerPort();
305             log.info("Setup is Custom, Dissociated & Asynchronous. Bootstrap message broker using URL: " + brokerUrl);
306             bootstrapManager.bootstrapMessaging(brokerUrl, true);
307             log.info("Embedded messaging system has been bootstrapped on port " + sForm.getBrokerPort());
308             DatabaseDetails details = DatabaseDetails.getDefault("hsqldb");
309             bootstrapManager.bootstrapDatabase(details, true);
310             log.info("Embedded database has been bootstrapped (mandatory for registring Administrator).");
311             return mapping.findForward("CustomAdmin");
312          }
313       }
314       // This should not happen...
315       return null;
316    }
317 
318    /**
319     * Register administrator user into datastore. If setup type is standard,
320     * the embedded database should first be boostrapped.
321     * @return A forward to the next view to render and display
322     */
323    protected ActionForward setupAdministrator(ActionMapping mapping, ActionForm form, HttpServletRequest request,
324                                               HttpServletResponse response) throws Exception{
325       if (form instanceof SetupForm){
326          // Context may have been refreshed and this action may be provided by Spring.
327          // If we are in this case, we have to reload setup type for future forwards.
328          if (setupType == null){
329             log.info("Current setup type is null. Refreshing it from boostrap manager.");
330             setupType = bootstrapManager.getProperty(ApplicationConfig.SETUP_TYPE);
331             log.info("Current setup type is now: '" + setupType + "'");
332          }
333 
334          // We can now create administrator.
335          SetupForm sForm = (SetupForm)form;
336          User admin = new User(sForm.getAdminName(), sForm.getAdminPassword(),
337                                           sForm.getAdminLastname(), sForm.getAdminFirstname());
338 
339          try {userManager.saveUser(admin);}
340          catch (DuplicateEntityException dee){
341             // Store exception within request.
342             request.setAttribute("duplicate", dee.getOriginalEntity());
343             return forward(mapping);
344          }
345 
346          // Create default roles and permissions.
347          createDefaultRolesAndPermissions(admin);
348          // Import default entities into database.
349          importDefaultEntitiesIntoDatabase();
350          // Login administrator.
351          UserView view = userManager.login(admin.getLogin(), sForm.getAdminPassword());
352          getUserContainer(request).setView(view);
353 
354          // This is the end of the setup process. Just call the finishSetup() method.
355          return finishSetup(mapping, form, request, response);
356       }
357       // This should not happen...
358       return null;
359    }
360 
361    /**
362     * Setup the embedded database and bootstrap Hibernate.
363     * @return A forward to the next view to render and display
364     */
365    protected ActionForward setupEmbedded(ActionMapping mapping, ActionForm form,
366                                          HttpServletRequest request, HttpServletResponse response) throws Exception{
367       if (form instanceof SetupForm){
368          log.info("Setup is Custom with embedded database. Bootstrapping it.");
369          DatabaseDetails details = DatabaseDetails.getDefault("hsqldb");
370          bootstrapManager.bootstrapDatabase(details, true);
371          log.info("Embedded database has been bootstrapped.");
372          // Forward to admin cutomisation.
373          return mapping.findForward("CustomAdmin");
374       }
375       // This should not happen...
376       return null;
377    }
378    
379    /**
380     * Setup the database access properties and bootstrap Hibernate.
381     * @return A forward to the next view to render and display
382     */
383    protected ActionForward setupDatabase(ActionMapping mapping, ActionForm form,
384                                          HttpServletRequest request, HttpServletResponse response) throws Exception{
385       if (form instanceof SetupForm){
386          // Create database details from form.
387          SetupForm sForm = (SetupForm)form;
388          DatabaseDetails details = new DatabaseDetails();
389          // Fill in details.
390          details.setUsername(sForm.getUserName());
391          details.setPassword(sForm.getUserPassword());
392          details.setDatabaseUrl(sForm.getDatabaseUrl());
393          details.setDriverClassname(sForm.getDriverClassname());
394          details.setDialect(sForm.getDialect());
395          details.setPoolSize(sForm.getPoolSize());
396          log.info("Setup is Custom with external database. Bootstrapping it on URL: " + details.getDatabaseUrl());
397          try {bootstrapManager.bootstrapDatabase(details, false);}
398          catch (Exception e){
399             // Create error message using Struts mechanism.
400             ActionErrors errors = new ActionErrors();
401             ActionMessage msg = new ActionMessage(e.getMessage());
402             errors.add(ActionErrors.GLOBAL_MESSAGE, msg);
403             // Save our error messages and return to the input form if possible
404             request.setAttribute(Globals.ERROR_KEY, errors);
405             return mapping.findForward("Database");
406          }
407          // Forward to admin customisation.
408          return mapping.findForward("CustomAdmin");
409       }
410       // This should not happen...
411       return null;
412    }
413    
414    /**
415     * Setup the database access properties through datasource and bootstrap Hibernate.
416     * @return A forward to the next view to render and display
417     */
418    protected ActionForward setupDatasource(ActionMapping mapping, ActionForm form,
419                                            HttpServletRequest request, HttpServletResponse response) throws Exception{
420       if (form instanceof SetupForm){
421          // Get datasource details from form.
422          SetupForm sForm = (SetupForm)form;
423          String datasourceName = sForm.getDatasource();
424          // Bottstrap on datasource.
425          log.info("Setup is Custom with external datasource. Bootstrapping it on JNDI ds: " + datasourceName);
426          try {bootstrapManager.bootstrapDatasource(datasourceName, sForm.getDialect());}
427          catch (Exception e){
428             // Create error message using Struts mechanism.
429             ActionErrors errors = new ActionErrors();
430             ActionMessage msg = new ActionMessage(e.getMessage());
431             errors.add(ActionErrors.GLOBAL_MESSAGE, msg);
432             // Save our error messages and return to the input form if possible
433             request.setAttribute(Globals.ERROR_KEY, errors);
434             return mapping.findForward("Database");
435          }
436          // Forward to admin customisation.
437          return mapping.findForward("CustomAdmin");
438       }
439       // This should not happen...
440       return null;
441    }
442 
443    /**
444     * Finish the setup process by telling the configuration manager that setup is
445     * now complete. Save configuration and put context & request attributes telling
446     * so before forwarding.
447     * @return A forward to the next view to render and display
448     */
449    protected ActionForward finishSetup(ActionMapping mapping, ActionForm form,
450                                        HttpServletRequest request, HttpServletResponse response) throws Exception{
451       if (form instanceof SetupForm){
452          log.info("Setup is complete. Saving application configuration...");
453          // Tell the configuration that setup is complete and save it.
454          appConfiguration.setSetupComplete(true);
455          appConfiguration.save();
456          log.info("Application configuration has been saved.");
457 
458          // Put context & request attributes before forwarding.
459          servlet.getServletContext().setAttribute(ApplicationConfig.SETUP_COMPLETE, "true");
460          request.setAttribute("step", "finish");
461          return forward(mapping);
462       }
463       // This should not happen...
464       return null;
465    }
466 
467    /** @return True if the current setup type is a custom one */
468    protected boolean isCustomSetup(){
469       return (setupType != null && setupType.equals(ApplicationConfig.CUSTOM_TYPE));
470    }
471 
472    /** @return True if the current setup type is a standard one */
473    protected boolean isStandardSetup(){
474       return (setupType != null && setupType.equals(ApplicationConfig.STANDARD_TYPE));
475    }
476 
477 
478    // Private ------------------------------------------------------------------
479 
480    /**
481     * @param mapping The action mapping repository
482     * @return An ActionForward corresponding to current setup type
483     */
484    private ActionForward forward(ActionMapping mapping){
485       if (isCustomSetup())
486          return mapping.findForward("Custom");
487       else if (isStandardSetup())
488          return mapping.findForward("Standard");
489       else
490          return mapping.findForward("Setup");
491    }
492 
493    /**
494     * Creates the Join default security roles and permission for admin
495     * @param admin The newly created administration user.
496     */
497    private void createDefaultRolesAndPermissions(User admin) throws DuplicateEntityException{
498       // Create defaults roles.
499       permissionManager.saveRole(new Role(Role.DEFAULT_JOINER_ROLE, "Integration Team Member"));
500       permissionManager.saveRole(new Role(Role.DEFAULT_ARCHITECT_ROLE, "Software Project Architect"));
501 
502       // Create administrator role and permission.
503       Role administrator = new Role(Role.DEFAULT_ADMIN_ROLE, "Join Application Administrator");
504       permissionManager.saveRole(administrator);
505       permissionManager.savePermission(new Permission(administrator, admin.getLogin()));
506    }
507 
508    /**
509     * Import the default entities contained into an Xml file
510     * @throws InfrastructureException if those entities cannot be created
511     */
512    private void importDefaultEntitiesIntoDatabase() throws InfrastructureException{
513       try{
514          InputStream is = getClass().getResourceAsStream("/join-entities.xml");
515          Collection entities = dataBinder.loadFromXml(is);
516          // Try replicating each objects.
517          Iterator iterator = entities.iterator();
518          while (iterator.hasNext()){
519             Object entity = iterator.next();
520             genericDao.replicate(entity);
521          }
522       }
523       catch (IOException ioe){
524          // Log and wrap into InfrastructureException.
525          log.error("IOException while importing entities from /join-entities.xml");
526          log.error("Here's the detailed message: " + ioe.getMessage());
527          throw new InfrastructureException("IOException while importing default entities into database", ioe);
528       }
529    }
530 }