View Javadoc

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