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
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
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
117
118 /** Creates a new instance of SetupActions */
119 public SetupActions(){
120 }
121
122
123
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
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
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
199 return null;
200 }
201
202
203
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
214
215 setBootstrapManager(BootstrapUtil.getBootstrapManager());
216
217
218 setupType = ((SetupForm)form).getSetupType();
219 bootstrapManager.setProperty(ApplicationConfig.SETUP_TYPE, setupType);
220 log.info("Current setup type is now: '" + setupType + "'");
221
222
223
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
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
250 bootstrapManager.setProperty(ApplicationConfig.DISSOCIATED, isDissociated);
251
252 if (bootstrapManager.isDissociatedSetup()){
253
254 log.info("Setup is custom & dissociated. Choose now the side of installation...");
255 return mapping.findForward("Dissociated");
256 }
257 else{
258
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
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
278 bootstrapManager.setProperty(ApplicationConfig.SYNCH_SIDE, isSynchronousSide);
279
280 return mapping.findForward("OtherSideAccess");
281 }
282
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
295 if (!bootstrapManager.isAsynchronousSide()){
296
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
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
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
327
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
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
342 request.setAttribute("duplicate", dee.getOriginalEntity());
343 return forward(mapping);
344 }
345
346
347 createDefaultRolesAndPermissions(admin);
348
349 importDefaultEntitiesIntoDatabase();
350
351 UserView view = userManager.login(admin.getLogin(), sForm.getAdminPassword());
352 getUserContainer(request).setView(view);
353
354
355 return finishSetup(mapping, form, request, response);
356 }
357
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
373 return mapping.findForward("CustomAdmin");
374 }
375
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
387 SetupForm sForm = (SetupForm)form;
388 DatabaseDetails details = new DatabaseDetails();
389
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
400 ActionErrors errors = new ActionErrors();
401 ActionMessage msg = new ActionMessage(e.getMessage());
402 errors.add(ActionErrors.GLOBAL_MESSAGE, msg);
403
404 request.setAttribute(Globals.ERROR_KEY, errors);
405 return mapping.findForward("Database");
406 }
407
408 return mapping.findForward("CustomAdmin");
409 }
410
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
422 SetupForm sForm = (SetupForm)form;
423 String datasourceName = sForm.getDatasource();
424
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
429 ActionErrors errors = new ActionErrors();
430 ActionMessage msg = new ActionMessage(e.getMessage());
431 errors.add(ActionErrors.GLOBAL_MESSAGE, msg);
432
433 request.setAttribute(Globals.ERROR_KEY, errors);
434 return mapping.findForward("Database");
435 }
436
437 return mapping.findForward("CustomAdmin");
438 }
439
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
454 appConfiguration.setSetupComplete(true);
455 appConfiguration.save();
456 log.info("Application configuration has been saved.");
457
458
459 servlet.getServletContext().setAttribute(ApplicationConfig.SETUP_COMPLETE, "true");
460 request.setAttribute("step", "finish");
461 return forward(mapping);
462 }
463
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
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
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
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
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
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 }