View Javadoc

1   /**
2    * This file Copyright (c) 2003-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module;
35  
36  import info.magnolia.cms.beans.config.ContentRepository;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.HierarchyManager;
39  import info.magnolia.cms.core.SystemProperty;
40  import info.magnolia.cms.util.ObservationUtil;
41  import info.magnolia.cms.util.SystemContentWrapper;
42  import info.magnolia.content2bean.Content2BeanException;
43  import info.magnolia.content2bean.Content2BeanUtil;
44  import info.magnolia.context.MgnlContext;
45  import info.magnolia.module.delta.Condition;
46  import info.magnolia.module.delta.Delta;
47  import info.magnolia.module.delta.Task;
48  import info.magnolia.module.delta.TaskExecutionException;
49  import info.magnolia.module.model.ModuleDefinition;
50  import info.magnolia.module.model.RepositoryDefinition;
51  import info.magnolia.module.model.Version;
52  import info.magnolia.module.model.reader.BetwixtModuleDefinitionReader;
53  import info.magnolia.module.model.reader.DependencyChecker;
54  import info.magnolia.module.model.reader.DependencyCheckerImpl;
55  import info.magnolia.module.model.reader.ModuleDefinitionReader;
56  import info.magnolia.module.ui.ModuleManagerNullUI;
57  import info.magnolia.module.ui.ModuleManagerUI;
58  import info.magnolia.module.ui.ModuleManagerWebUI;
59  import info.magnolia.objectfactory.ClassFactory;
60  import info.magnolia.objectfactory.Classes;
61  import info.magnolia.objectfactory.Components;
62  import info.magnolia.objectfactory.MgnlInstantiationException;
63  import info.magnolia.repository.Provider;
64  import info.magnolia.repository.RepositoryConstants;
65  
66  import java.util.ArrayList;
67  import java.util.Collection;
68  import java.util.Collections;
69  import java.util.HashMap;
70  import java.util.Iterator;
71  import java.util.List;
72  import java.util.Map;
73  
74  import javax.inject.Inject;
75  import javax.inject.Singleton;
76  import javax.jcr.RepositoryException;
77  import javax.jcr.observation.EventIterator;
78  import javax.jcr.observation.EventListener;
79  
80  import org.apache.commons.beanutils.BeanUtils;
81  import org.apache.commons.lang.StringUtils;
82  import org.apache.commons.lang.exception.ExceptionUtils;
83  
84  /**
85   * TODO : factor out into simpler units.
86   *
87   * @author gjoseph
88   * @version $Revision: $ ($Author: $)
89   */
90  @Singleton
91  public class ModuleManagerImpl implements ModuleManager {
92  
93      private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ModuleManagerImpl.class);
94  
95      private static final int DEFAULT_MODULE_OBSERVATION_DELAY = 5000;
96      private static final int DEFAULT_MODULE_OBSERVATION_MAX_DELAY = 30000;
97  
98      // TODO : expose a method to retrieve a given module's node ?
99      // TODO : see InstallContextImpl.getOrCreateCurrentModuleConfigNode()
100     static final String MODULES_NODE = "modules";
101 
102     /**
103      * List<ModuleDefinition> of modules found to be deployed.
104      */
105     private List<ModuleDefinition> orderedModuleDescriptors;
106 
107     private ModuleManagementState state;
108 
109     // here we use the implementation, since it has extra methods that should not be exposed to Task methods.
110     private final InstallContextImpl installContext;
111 
112     private final ModuleRegistry registry;
113     private final ModuleDefinitionReader moduleDefinitionReader;
114     private final DependencyChecker dependencyChecker;
115 
116     /**
117      * @deprecated since 4.5 - use IoC - temporarily kept for tests ?
118      */
119     protected ModuleManagerImpl() {
120         this(new InstallContextImpl(ModuleRegistry.Factory.getInstance()), new BetwixtModuleDefinitionReader());
121     }
122 
123     /**
124      * @deprecated since 4.5 - use IoC - temporarily kept for tests ?
125      */
126     protected ModuleManagerImpl(InstallContextImpl installContext, ModuleDefinitionReader moduleDefinitionReader) {
127         this(installContext, moduleDefinitionReader, ModuleRegistry.Factory.getInstance(), new DependencyCheckerImpl());
128     }
129 
130     @Inject
131     public ModuleManagerImpl(InstallContextImpl installContext, ModuleDefinitionReader moduleDefinitionReader, ModuleRegistry moduleRegistry, DependencyChecker dependencyChecker) {
132         this.installContext = installContext;
133         this.moduleDefinitionReader = moduleDefinitionReader;
134         this.registry = moduleRegistry;
135         this.dependencyChecker = dependencyChecker;
136     }
137 
138     @Override
139     public List<ModuleDefinition> loadDefinitions() throws ModuleManagementException {
140         if (state != null) {
141             throw new IllegalStateException("ModuleManager was already initialized !");
142         }
143 
144         final Map<String, ModuleDefinition> moduleDefinitions = moduleDefinitionReader.readAll();
145         if (moduleDefinitions.isEmpty()) {
146             throw new ModuleManagementException("No module definition was found.");
147         }
148         log.debug("Loaded definitions: {}", moduleDefinitions);
149 
150         dependencyChecker.checkDependencies(moduleDefinitions);
151         orderedModuleDescriptors = dependencyChecker.sortByDependencyLevel(moduleDefinitions);
152         for (ModuleDefinition moduleDefinition : orderedModuleDescriptors) {
153             registry.registerModuleDefinition(moduleDefinition.getName(), moduleDefinition);
154         }
155         return orderedModuleDescriptors;
156     }
157 
158     /**
159      * In addition to checking for install or updates, this method also loads
160      * repositories when there are no pending install or update tasks.
161      *
162      * @see info.magnolia.module.ModuleManager#checkForInstallOrUpdates()
163      */
164     @Override
165     public void checkForInstallOrUpdates() {
166         // compare and determine if we need to do anything
167         state = new ModuleManagementState();
168         int taskCount = 0;
169         for (ModuleDefinition module : orderedModuleDescriptors) {
170             installContext.setCurrentModule(module);
171             log.debug("Checking for installation or update [{}]", module);
172             final ModuleVersionHandler versionHandler = newVersionHandler(module);
173             registry.registerModuleVersionHandler(module.getName(), versionHandler);
174 
175             final Version currentVersion = versionHandler.getCurrentlyInstalled(installContext);
176             final List<Delta> deltas = versionHandler.getDeltas(installContext, currentVersion);
177             if (deltas.size() > 0) {
178                 state.addModule(module, currentVersion, deltas);
179                 for (Delta delta : deltas) {
180                     taskCount += delta.getTasks().size();
181                 }
182             }
183         }
184         // TODO handle modules found in repo but not found on classpath
185 
186         installContext.setCurrentModule(null);
187         installContext.setTotalTaskCount(taskCount);
188 
189         // if we don't have to perform any update load repositories now
190         if (!state.needsUpdateOrInstall()) {
191             loadModulesRepositories();
192         }
193 
194         // TODO : check the force bootstrap properties
195     }
196 
197     @Override
198     public ModuleManagementState getStatus() {
199         if (state == null) {
200             throw new IllegalStateException("ModuleManager was not initialized !");
201         }
202 
203         return state;
204     }
205 
206     @Override
207     public ModuleManagerUI getUI() {
208         if (SystemProperty.getBooleanProperty("magnolia.update.auto")) {
209             return new ModuleManagerNullUI(this);
210         }
211         return new ModuleManagerWebUI(this);
212     }
213 
214     protected ModuleVersionHandler newVersionHandler(ModuleDefinition module) {
215         try {
216             final Class<? extends ModuleVersionHandler> versionHandlerClass = module.getVersionHandler();
217             if (versionHandlerClass != null) {
218                 return Classes.getClassFactory().newInstance(versionHandlerClass);
219             }
220             return new DefaultModuleVersionHandler();
221         } catch (MgnlInstantiationException e) {
222             throw e; // TODO
223         }
224     }
225 
226     @Override
227     public void performInstallOrUpdate() {
228         synchronized (installContext) {
229             if (state == null) {
230                 throw new IllegalStateException("ModuleManager was not initialized !");
231             }
232             if (!state.needsUpdateOrInstall()) {
233                 throw new IllegalStateException("ModuleManager has nothing to do !");
234             }
235             if (installContext.getStatus() != null) {
236                 throw new IllegalStateException("ModuleManager.performInstallOrUpdate() was already started !");
237             }
238             installContext.setStatus(InstallStatus.inProgress);
239         }
240 
241         // check all conditions
242         boolean conditionsChecked = true;
243         for (ModuleAndDeltas moduleAndDeltas : state.getList()) {
244             // TODO extract "do for all deltas" logic ?
245             installContext.setCurrentModule(moduleAndDeltas.getModule());
246             for (Delta delta : moduleAndDeltas.getDeltas()) {
247                 final List<Condition> conditions = delta.getConditions();
248                 for (Condition cond : conditions) {
249                     if (!cond.check(installContext)) {
250                         conditionsChecked = false;
251                         installContext.warn(cond.getDescription());
252                     }
253                 }
254             }
255         }
256         installContext.setCurrentModule(null);
257         if (!conditionsChecked) {
258             installContext.setStatus(InstallStatus.stoppedConditionsNotMet);
259             return;
260         }
261 
262         loadModulesRepositories();
263 
264         MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
265             @Override
266             public void doExec() {
267                 final Iterator<ModuleAndDeltas> it = state.getList().iterator();
268                 while (it.hasNext()) {
269                     final ModuleAndDeltas moduleAndDeltas = it.next();
270                     installOrUpdateModule(moduleAndDeltas, installContext);
271                     it.remove();
272                 }
273             }
274         }, true);
275 
276         // TODO : this isn't super clean.
277         final InstallStatus status = installContext.isRestartNeeded() ? InstallStatus.installDoneRestartNeeded : InstallStatus.installDone;
278         installContext.setStatus(status);
279     }
280 
281     @Override
282     public InstallContext getInstallContext() {
283         return installContext;
284     }
285 
286     @Override
287     public void startModules() {
288         // process startup tasks before actually starting modules
289         executeStartupTasks();
290 
291         // here we use the implementation, since it has extra methods that should not be exposed to ModuleLifecycle methods.
292         // TODO we should keep only one instance of the lifecycle context
293         final ModuleLifecycleContextImpl lifecycleContext = new ModuleLifecycleContextImpl();
294         lifecycleContext.setPhase(ModuleLifecycleContext.PHASE_SYSTEM_STARTUP);
295         final HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(RepositoryConstants.CONFIG);
296         Content modulesParentNode;
297         try {
298             modulesParentNode = hm.getContent(MODULES_NODE);
299         }
300         catch (RepositoryException e) {
301             throw new RuntimeException("Can't start module due to failing to load the /modules node.",e);
302         }
303         final Collection<Content> moduleNodes = new ArrayList<Content>();
304 
305         for (ModuleDefinition moduleDefinition : orderedModuleDescriptors) {
306             final String moduleClassName = moduleDefinition.getClassName();
307             final String moduleName = moduleDefinition.getName();
308             log.info("Initializing module {}", moduleName);
309 
310             try {
311                 final Object moduleInstance;
312                 if (moduleClassName != null) {
313                     try {
314                         final ClassFactory classFactory = Classes.getClassFactory();
315                         final Class<?> moduleClass = classFactory.forName(moduleClassName);
316                         // We use the currently set ComponentProvider which is main
317                         moduleInstance = Components.newInstance(moduleClass);
318                     } catch (Throwable t) {
319                         log.error("Can't instantiate " + moduleClassName + " for module " + moduleName + " : " + t.getClass() + " : " + t.getMessage(), t);
320                         continue;
321                     }
322                     // Still registering module instances with ModuleRegistry, although now one should use IoC and *depend* on such objects instead.
323                     registry.registerModuleInstance(moduleName, moduleInstance);
324                 } else {
325                     moduleInstance = null;
326                 }
327 
328                 if (modulesParentNode.hasContent(moduleName)) {
329                     moduleNodes.add(new SystemContentWrapper(modulesParentNode.getContent(moduleName)));
330                 }
331 
332                 if (moduleInstance != null) {
333 
334                     populateModuleInstance(moduleInstance, getModuleInstanceProperties(moduleDefinition));
335 
336                     startModule(moduleInstance, moduleDefinition, lifecycleContext);
337 
338                     // start observation
339                     ObservationUtil.registerDeferredChangeListener(RepositoryConstants.CONFIG, "/modules/" + moduleName + "/config", new EventListener() {
340 
341                         @Override
342                         public void onEvent(EventIterator events) {
343                             final Object moduleInstance = registry.getModuleInstance(moduleName);
344                             final ModuleDefinition moduleDefinition = registry.getDefinition(moduleName);
345 
346                             // TODO we should keep only one instance of the lifecycle context
347                             final ModuleLifecycleContextImpl lifecycleContext = new ModuleLifecycleContextImpl();
348                             lifecycleContext.setPhase(ModuleLifecycleContext.PHASE_MODULE_RESTART);
349                             MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
350                                 @Override
351                                 public void doExec() {
352                                     stopModule(moduleInstance, moduleDefinition, lifecycleContext);
353                                     populateModuleInstance(moduleInstance, getModuleInstanceProperties(moduleDefinition));
354                                     startModule(moduleInstance, moduleDefinition, lifecycleContext);
355                                 }
356                             }, true);
357                         }
358                     }, DEFAULT_MODULE_OBSERVATION_DELAY, DEFAULT_MODULE_OBSERVATION_MAX_DELAY);
359                 }
360             }
361             catch (Throwable th) {
362                 log.error("Can't start module " + moduleName, th);
363             }
364         }
365 
366         lifecycleContext.start(moduleNodes);
367     }
368 
369     /**
370      * Process startup tasks. Tasks retured by <code>ModuleDefinition.getStartupTasks()</code> are always executed and
371      * do not require manual intervention.
372      */
373     protected void executeStartupTasks() {
374         MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
375             @Override
376             public void doExec() {
377                 for (ModuleDefinition module : orderedModuleDescriptors) {
378                     final ModuleVersionHandler versionHandler = registry.getVersionHandler(module.getName());
379                     installContext.setCurrentModule(module);
380                     final Delta startup = versionHandler.getStartupDelta(installContext);
381                     applyDeltas(module, Collections.singletonList(startup), installContext);
382                 }
383             }
384         }, false);
385     }
386 
387     protected void startModule(Object moduleInstance, final ModuleDefinition moduleDefinition, final ModuleLifecycleContextImpl lifecycleContext) {
388         if (moduleInstance instanceof ModuleLifecycle) {
389             lifecycleContext.setCurrentModuleDefinition(moduleDefinition);
390             log.info("Starting module {}", moduleDefinition.getName());
391             ((ModuleLifecycle) moduleInstance).start(lifecycleContext);
392         }
393     }
394 
395     protected void stopModule(Object moduleInstance, final ModuleDefinition moduleDefinition, final ModuleLifecycleContextImpl lifecycleContext) {
396         if (moduleInstance instanceof ModuleLifecycle) {
397             lifecycleContext.setCurrentModuleDefinition(moduleDefinition);
398             log.info("Stopping module {}", moduleDefinition.getName());
399             ((ModuleLifecycle) moduleInstance).stop(lifecycleContext);
400         }
401     }
402 
403     /**
404      * Builds a map of properties to be set on the module instance, the properties are "moduleDefinition", "name",
405      * "moduleNode" and "configNode". This map is rebuilt every-time reloading takes place just in case the nodes have
406      * changed since startup. One situation where this is necessary is when a module does not have a config node at
407      * startup but one is added later on, see MAGNOLIA-3457.
408      */
409     protected Map<String, Object> getModuleInstanceProperties(ModuleDefinition moduleDefinition) {
410 
411         final HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(RepositoryConstants.CONFIG);
412 
413         final String moduleNodePath = "/modules/" + moduleDefinition.getName();
414         Content moduleNode = null;
415         try {
416             moduleNode = hm.isExist(moduleNodePath) ? new SystemContentWrapper(hm.getContent(moduleNodePath)) : null;
417         } catch (RepositoryException e) {
418             log.error("Wasn't able to acquire module node " + moduleNodePath + ": " + e.getMessage(), e);
419         }
420 
421         final String moduleConfigPath = "/modules/" + moduleDefinition.getName() + "/config";
422         Content configNode = null;
423         try {
424             configNode = hm.isExist(moduleConfigPath) ? new SystemContentWrapper(hm.getContent(moduleConfigPath)) : null;
425         } catch (RepositoryException e) {
426             log.error("Wasn't able to acquire module config node " + moduleConfigPath + ": " + e.getMessage(), e);
427         }
428 
429         final Map<String, Object> moduleProperties = new HashMap<String, Object>();
430         moduleProperties.put("moduleDefinition", moduleDefinition);
431         moduleProperties.put("name", moduleDefinition.getName());
432         moduleProperties.put("moduleNode", moduleNode);
433         moduleProperties.put("configNode", configNode);
434 
435         return moduleProperties;
436     }
437 
438     protected void populateModuleInstance(Object moduleInstance, Map<String, Object> moduleProperties) {
439 
440         try {
441             BeanUtils.populate(moduleInstance, moduleProperties);
442         }
443         catch (Throwable e) {
444             log.error("Can't initialize module " + moduleInstance + ": " + e.getMessage(), e);
445         }
446 
447         if (moduleProperties.get("configNode") != null) {
448             try {
449                 Content2BeanUtil.setProperties(moduleInstance, (Content) moduleProperties.get("configNode"), true);
450             }
451             catch (Content2BeanException e) {
452                 log.error("Wasn't able to configure module " + moduleInstance + ": " + e.getMessage(), e);
453             }
454         }
455     }
456 
457     @Override
458     public void stopModules() {
459         // TODO we should keep only one instance of the lifecycle context
460         final ModuleLifecycleContextImpl lifecycleContext = new ModuleLifecycleContextImpl();
461         lifecycleContext.setPhase(ModuleLifecycleContext.PHASE_SYSTEM_SHUTDOWN);
462         if (orderedModuleDescriptors != null) {
463             // if module descriptors were read, let's shut down modules in reverse order
464             final ArrayList<ModuleDefinition> shutdownOrder = new ArrayList<ModuleDefinition>(orderedModuleDescriptors);
465             Collections.reverse(shutdownOrder);
466             for (ModuleDefinition md : shutdownOrder) {
467                 Object module = registry.getModuleInstance(md.getName());
468                 if (module instanceof ModuleLifecycle) {
469                     stopModule(module, md, lifecycleContext);
470                 }
471 
472             }
473         }
474     }
475 
476     protected void installOrUpdateModule(ModuleAndDeltas moduleAndDeltas, InstallContextImpl ctx) {
477         final ModuleDefinition moduleDef = moduleAndDeltas.getModule();
478         final List<Delta> deltas = moduleAndDeltas.getDeltas();
479         ctx.setCurrentModule(moduleDef);
480         log.debug("Install/update for {} is starting: {}", moduleDef, moduleAndDeltas);
481         applyDeltas(moduleDef, deltas, ctx);
482         log.debug("Install/update for {} has finished", moduleDef, moduleAndDeltas);
483     }
484 
485     /**
486      * Applies to given deltas for the given module. It is NOT responsible for setting the given
487      * module as being the current module in the given context, but it is responsible for unsetting
488      * it when done, and for saving upon success.
489      */
490     protected void applyDeltas(ModuleDefinition moduleDef, List<Delta> deltas, InstallContextImpl ctx) {
491         boolean success = true;
492         Task currentTask = null;
493         try {
494             for (Delta delta : deltas) {
495                 final List<Task> tasks = delta.getTasks();
496                 for (Task task : tasks) {
497                     currentTask = task;
498                     log.debug("Module {}, executing {}", moduleDef, currentTask);
499                     task.execute(ctx);
500                     ctx.incExecutedTaskCount();
501                 }
502             }
503         } catch (TaskExecutionException e) {
504             ctx.error("Could not install or update " + moduleDef.getName() + " module. Task '" + currentTask.getName() + "' failed. (" + ExceptionUtils.getRootCauseMessage(e) + ")", e);
505             success = false;
506         } catch (RuntimeException e) {
507             ctx.error("Error while installing or updating " + moduleDef.getName() + " module. Task '" + currentTask.getName() + "' failed. (" + ExceptionUtils.getRootCauseMessage(e) + ")", e);
508             throw e;
509         } finally {
510             // TODO : ctx.info("Successful installation/update."); after save ?
511             ctx.setCurrentModule(null);
512         }
513 
514         saveChanges(success);
515     }
516 
517     /**
518      * Save changes to jcr, or revert them if something went wrong.
519      * @param persist if <code>true</code>, all workspaces are save; if <code>false</code> changes will be reverted.
520      */
521     private void saveChanges(boolean persist) {
522         // save all repositories once a module was properly installed/updated, or rollback changes.
523         final Iterator<String> reposIt = ContentRepository.getAllRepositoryNames();
524         while (reposIt.hasNext()) {
525             final String repoName = reposIt.next();
526             log.debug((persist ? "Saving" : "Rolling back") + " repository " + repoName);
527             final HierarchyManager hm = MgnlContext.getHierarchyManager(repoName);
528             try {
529                 // don't call save or refresh if useless
530                 if (hm.getWorkspace().getSession().hasPendingChanges()) {
531                     if (persist) {
532                         hm.save();
533                     }
534                     else {
535                         hm.refresh(false);
536                     }
537                 }
538             }
539             catch (RepositoryException e) {
540                 throw new RuntimeException(e); // TODO
541             }
542         }
543     }
544 
545     /**
546      * Initializes repositories and workspaces defined by modules.
547      * Perform repository registration tasks (create repositories or workspace, setup nodetypes) that should be done
548      * always before starting the new module.
549      */
550     private void loadModulesRepositories() {
551         for (ModuleDefinition def : orderedModuleDescriptors) {
552             // register repositories
553             for (final RepositoryDefinition repDef : def.getRepositories()) {
554                 final String repositoryName = repDef.getName();
555 
556                 final String nodetypeFile = repDef.getNodeTypeFile();
557 
558                 final List<String> wsList = repDef.getWorkspaces();
559                 String[] workSpaces = wsList.toArray(new String[wsList.size()]);
560 
561                 loadRepository(repositoryName, nodetypeFile, workSpaces);
562             }
563         }
564     }
565 
566     /**
567      * Loads a single repository plus its workspaces, register nodetypes and grant permissions to superuser.
568      */
569     private void loadRepository(String repositoryNameFromModuleDescriptor, String nodeTypeFile, String[] workspaces) {
570 
571         if (workspaces == null || workspaces.length == 0)
572         {
573             log.error("Trying to register the repository {} without any workspace.", repositoryNameFromModuleDescriptor);
574             return;
575         }
576 
577         final String DEFAULT_REPOSITORY_NAME = "magnolia";
578         String repositoryName = repositoryNameFromModuleDescriptor;
579 
580         if (workspaces.length > 0) {
581             // get the repository name from the mapping, users may want to manually add it here if needed
582             info.magnolia.repository.definition.RepositoryDefinition repositoryMapping = ContentRepository.getRepositoryMapping(workspaces[0]);
583             if (repositoryMapping != null) {
584                 repositoryName = repositoryMapping.getName();
585             }
586         }
587 
588         info.magnolia.repository.definition.RepositoryDefinition rm = ContentRepository.getRepositoryMapping(repositoryName);
589 
590         if (rm == null) {
591 
592             final info.magnolia.repository.definition.RepositoryDefinition defaultRepositoryMapping = ContentRepository.getRepositoryMapping(DEFAULT_REPOSITORY_NAME);
593             final Map<String, String> defaultParameters = defaultRepositoryMapping.getParameters();
594 
595             rm = new info.magnolia.repository.definition.RepositoryDefinition();
596             rm.setName(repositoryName);
597             rm.setProvider(defaultRepositoryMapping.getProvider());
598             rm.setLoadOnStartup(true);
599 
600             final Map<String, String> parameters = new HashMap<String, String>();
601             parameters.putAll(defaultParameters);
602 
603             // override changed parameters
604             final String bindName = repositoryName + StringUtils.replace(defaultParameters.get("bindName"), "magnolia", "");
605             final String repositoryHome = StringUtils.substringBeforeLast(defaultParameters.get("configFile"), "/")
606                 + "/"
607                 + repositoryName;
608 
609             parameters.put("repositoryHome", repositoryHome);
610             parameters.put("bindName", bindName);
611             parameters.put("customNodeTypes", nodeTypeFile);
612 
613             rm.setParameters(parameters);
614 
615             try {
616                 ContentRepository.loadRepository(rm);
617             } catch (Exception e) {
618                 log.error(e.getMessage(), e);
619             }
620         }
621 
622         if (nodeTypeFile != null) {
623             // register nodetypes
624             registerNodeTypeFile(repositoryName, nodeTypeFile);
625             // if this repo is not the default one, register nodetypes on default repo (MAGNOLIA-3189)
626             if (!DEFAULT_REPOSITORY_NAME.equals(repositoryName)) {
627                 registerNodeTypeFile(DEFAULT_REPOSITORY_NAME, nodeTypeFile);
628             }
629         }
630 
631         if (workspaces != null) {
632             for (String workspace : workspaces) {
633                 if (!rm.getWorkspaces().contains(workspace)) {
634                     log.debug("Loading new workspace: {}", workspace);
635 
636                     try {
637                         ContentRepository.loadWorkspace(repositoryName, workspace);
638                     }
639                     catch (RepositoryException e) {
640                         // should never happen, the only exception we can get here is during login
641                         log.error(e.getMessage(), e);
642                     }
643                 }
644             }
645         }
646 
647     }
648 
649     /**
650      * Register nodeType file in repository.
651      * @param repositoryName repository name
652      * @param nodeTypeFile nodeType file
653      */
654     private void registerNodeTypeFile(String repositoryName, String nodeTypeFile) {
655         Provider provider = ContentRepository.getRepositoryProvider(repositoryName);
656         try {
657             provider.registerNodeTypes(nodeTypeFile);
658         }
659         catch (RepositoryException e) {
660             log.error(e.getMessage(), e);
661         }
662     }
663 }