001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS <http://nuxeo.com> and others
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Jean-Marc Orliaguet, Chalmers
011 *
012 * $Id$
013 */
014
015package org.nuxeo.theme.services;
016
017import java.io.File;
018import java.io.IOException;
019import java.net.MalformedURLException;
020import java.net.URL;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.nuxeo.runtime.model.ComponentContext;
028import org.nuxeo.runtime.model.ComponentName;
029import org.nuxeo.runtime.model.DefaultComponent;
030import org.nuxeo.runtime.model.Extension;
031import org.nuxeo.runtime.model.RuntimeContext;
032import org.nuxeo.theme.ApplicationType;
033import org.nuxeo.theme.CachingDef;
034import org.nuxeo.theme.Manager;
035import org.nuxeo.theme.NegotiationDef;
036import org.nuxeo.theme.Registrable;
037import org.nuxeo.theme.RegistryType;
038import org.nuxeo.theme.ViewDef;
039import org.nuxeo.theme.engines.EngineType;
040import org.nuxeo.theme.models.ModelType;
041import org.nuxeo.theme.perspectives.PerspectiveType;
042import org.nuxeo.theme.presets.PaletteParser;
043import org.nuxeo.theme.presets.PaletteType;
044import org.nuxeo.theme.presets.PresetType;
045import org.nuxeo.theme.resources.BankImport;
046import org.nuxeo.theme.resources.BankManager;
047import org.nuxeo.theme.resources.ResourceBank;
048import org.nuxeo.theme.resources.ResourceType;
049import org.nuxeo.theme.templates.TemplateEngineType;
050import org.nuxeo.theme.themes.ThemeDescriptor;
051import org.nuxeo.theme.themes.ThemeIOException;
052import org.nuxeo.theme.themes.ThemeManager;
053import org.nuxeo.theme.themes.ThemeParser;
054import org.nuxeo.theme.themes.ThemeSet;
055import org.nuxeo.theme.themes.ThemeSetEntry;
056import org.nuxeo.theme.types.Type;
057import org.nuxeo.theme.types.TypeFamily;
058import org.nuxeo.theme.types.TypeRegistry;
059import org.nuxeo.theme.views.ViewType;
060
061public class ThemeService extends DefaultComponent {
062
063    public static final ComponentName ID = new ComponentName("org.nuxeo.theme.services.ThemeService");
064
065    private static final Log log = LogFactory.getLog(ThemeService.class);
066
067    private Map<String, Registrable> registries = new HashMap<String, Registrable>();
068
069    private RuntimeContext context;
070
071    public Map<String, Registrable> getRegistries() {
072        return registries;
073    }
074
075    public Registrable getRegistry(String name) {
076        return registries.get(name);
077    }
078
079    public synchronized void addRegistry(String name, Registrable registry) {
080        registries.put(name, registry);
081    }
082
083    public synchronized void removeRegistry(String name) {
084        Registrable registry = registries.get(name);
085        if (registry != null) {
086            registry.clear();
087        }
088        registries.remove(name);
089    }
090
091    @Override
092    public void activate(ComponentContext ctx) {
093        context = ctx.getRuntimeContext();
094        Manager.initializeProtocols();
095        log.debug("Theme service activated");
096    }
097
098    @Override
099    public void deactivate(ComponentContext ctx) {
100        for (Registrable registry : registries.values()) {
101            registry.clear();
102        }
103        registries.clear();
104        context = null;
105        Manager.resetProtocols();
106        log.debug("Theme service deactivated");
107    }
108
109    @Override
110    public void applicationStarted(ComponentContext context) {
111        // themes registered as contributions
112        for (ThemeDescriptor themeDescriptor : ThemeManager.getThemeDescriptors()) {
113            registerTheme(themeDescriptor);
114        }
115        // custom themes located on the file-system
116        registerCustomThemes();
117
118        ThemeManager.updateThemeDescriptors();
119
120        // setup resource banks
121        BankManager.setupBanks();
122    }
123
124    @Override
125    public void registerExtension(Extension extension) {
126        String xp = extension.getExtensionPoint();
127        if (xp.equals("registries")) {
128            registerRegistryExtension(extension);
129        } else if (xp.equals("elements") || xp.equals("fragments") || xp.equals("formats")
130                || xp.equals("format-filters") || xp.equals("standalone-filters") || xp.equals("negotiations")
131                || xp.equals("shortcuts") || xp.equals("vocabularies")) {
132            registerTypeExtension(extension);
133        } else if (xp.equals("applications")) {
134            registerApplicationExtension(extension);
135        } else if (xp.equals("perspectives")) {
136            registerPerspectiveExtension(extension);
137        } else if (xp.equals("engines")) {
138            registerEngineExtension(extension);
139        } else if (xp.equals("template-engines")) {
140            registerTemplateEngineExtension(extension);
141        } else if (xp.equals("themes")) {
142            registerThemeExtension(extension);
143        } else if (xp.equals("themesets")) {
144            registerThemeSetExtension(extension);
145        } else if (xp.equals("presets")) {
146            registerPresetExtension(extension);
147        } else if (xp.equals("views")) {
148            registerViewExtension(extension);
149        } else if (xp.equals("models")) {
150            registerModelExtension(extension);
151        } else if (xp.equals("resources")) {
152            registerResourceExtension(extension);
153        } else if (xp.equals("banks")) {
154            registerBank(extension);
155        } else {
156            log.warn(String.format("Unknown extension point: %s", xp));
157        }
158    }
159
160    @Override
161    public void unregisterExtension(Extension extension) {
162        String xp = extension.getExtensionPoint();
163        if (xp.equals("registries")) {
164            unregisterRegistryExtension(extension);
165        } else if (xp.equals("elements") || xp.equals("fragments") || xp.equals("formats")
166                || xp.equals("format-filters") || xp.equals("standalone-filters") || xp.equals("engines")
167                || xp.equals("template-engines") || xp.equals("negotiations") || xp.equals("perspectives")
168                || xp.equals("applications") || xp.equals("shortcuts") || xp.equals("vocabularies")
169                || (xp.equals("presets")) || xp.equals("views") || xp.equals("themes") || xp.equals("themesets")) {
170            unregisterTypeExtension(extension);
171        } else if (xp.equals("resources")) {
172            unregisterResourceExtension(extension);
173        } else if (xp.equals("views")) {
174            unregisterViewExtension(extension);
175        } else if (xp.equals("models")) {
176            unregisterModelExtension(extension);
177        } else if (xp.equals("banks")) {
178            unregisterBank(extension);
179        } else {
180            log.warn(String.format("Unknown extension point: %s", xp));
181        }
182    }
183
184    private void registerRegistryExtension(Extension extension) {
185        Object[] contribs = extension.getContributions();
186        for (Object contrib : contribs) {
187            RegistryType registryType = (RegistryType) contrib;
188            Registrable registry = null;
189            try {
190                registry = (Registrable) context.loadClass(registryType.getClassName()).newInstance();
191            } catch (ReflectiveOperationException e) {
192                log.warn("Could not create registry: " + registryType.getName() + "(" + registryType.getClassName()
193                        + ")");
194            }
195            if (registry != null) {
196                addRegistry(registryType.getName(), registry);
197            }
198        }
199    }
200
201    private void unregisterRegistryExtension(Extension extension) {
202        Object[] contribs = extension.getContributions();
203        for (Object contrib : contribs) {
204            RegistryType registryType = (RegistryType) contrib;
205            removeRegistry(registryType.getName());
206        }
207    }
208
209    private void registerTypeExtension(Extension extension) {
210        Object[] contribs = extension.getContributions();
211        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
212        for (Object contrib : contribs) {
213            typeRegistry.register((Type) contrib);
214        }
215    }
216
217    private void unregisterTypeExtension(Extension extension) {
218        Object[] contribs = extension.getContributions();
219        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
220        if (typeRegistry != null) {
221            for (Object contrib : contribs) {
222                typeRegistry.unregister((Type) contrib);
223            }
224        }
225    }
226
227    private void registerApplicationExtension(Extension extension) {
228        Object[] contribs = extension.getContributions();
229        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
230        for (Object contrib : contribs) {
231            ApplicationType application = (ApplicationType) contrib;
232
233            ApplicationType oldApplication = (ApplicationType) typeRegistry.lookup(TypeFamily.APPLICATION,
234                    application.getTypeName());
235
236            if (oldApplication == null) {
237                String templateEngine = application.getTemplateEngine();
238                if (templateEngine == null) {
239                    final String defaultTemplateEngine = ThemeManager.getDefaultTemplateEngineName();
240                    log.warn(String.format(
241                            "Please set the 'template-engine' attribute on <application root=\"%s\" template-engine=\"...\"> (default is '%s')",
242                            application.getRoot(), defaultTemplateEngine));
243                    application.setTemplateEngine(defaultTemplateEngine);
244                }
245                typeRegistry.register(application);
246
247            } else {
248                // Merge properties from already registered application
249                final String templateEngine = application.getTemplateEngine();
250                if (templateEngine != null) {
251                    oldApplication.setTemplateEngine(templateEngine);
252                }
253
254                NegotiationDef negotiation = application.getNegotiation();
255                if (negotiation != null) {
256                    NegotiationDef oldNegotiation = oldApplication.getNegotiation();
257                    if (oldNegotiation == null) {
258                        oldNegotiation = new NegotiationDef();
259                        oldApplication.setNegotiation(oldNegotiation);
260                    }
261                    if (negotiation.getStrategy() != null) {
262                        oldNegotiation.setStrategy(negotiation.getStrategy());
263                    }
264                    if (negotiation.getDefaultTheme() != null) {
265                        oldNegotiation.setDefaultTheme(negotiation.getDefaultTheme());
266                    }
267                    if (negotiation.getDefaultPerspective() != null) {
268                        oldNegotiation.setDefaultPerspective(negotiation.getDefaultPerspective());
269                    }
270                    if (negotiation.getDefaultEngine() != null) {
271                        oldNegotiation.setDefaultEngine(negotiation.getDefaultEngine());
272                    }
273                }
274
275                CachingDef resourceCaching = application.getResourceCaching();
276                if (resourceCaching != null) {
277                    CachingDef oldResourceCaching = oldApplication.getResourceCaching();
278                    if (oldResourceCaching == null) {
279                        oldResourceCaching = new CachingDef();
280                        oldApplication.setResourceCaching(oldResourceCaching);
281                    }
282                    if (resourceCaching.getLifetime() != null) {
283                        oldResourceCaching.setLifetime(resourceCaching.getLifetime());
284                    }
285                }
286
287                CachingDef styleCaching = application.getStyleCaching();
288                if (styleCaching != null) {
289                    CachingDef oldStyleCaching = oldApplication.getStyleCaching();
290                    if (oldStyleCaching == null) {
291                        oldStyleCaching = new CachingDef();
292                        oldApplication.setStyleCaching(oldStyleCaching);
293                    }
294                    if (styleCaching.getLifetime() != null) {
295                        oldStyleCaching.setLifetime(styleCaching.getLifetime());
296                    }
297                }
298
299                Map<String, ViewDef> viewDefs = application.getViewDefs();
300                if (!viewDefs.isEmpty()) {
301                    Map<String, ViewDef> oldViewDefs = oldApplication.getViewDefs();
302                    for (Map.Entry<String, ViewDef> entry : viewDefs.entrySet()) {
303                        oldViewDefs.put(entry.getKey(), entry.getValue());
304                    }
305                }
306
307            }
308        }
309    }
310
311    private void registerPerspectiveExtension(Extension extension) {
312        Object[] contribs = extension.getContributions();
313        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
314        for (Object contrib : contribs) {
315            PerspectiveType perspective = (PerspectiveType) contrib;
316            if (!perspective.getName().matches("[a-z0-9_\\-]+")) {
317                log.error("Perspective names may only contain lowercase alphanumeric characters, underscores and hyphens ");
318                continue;
319            }
320            typeRegistry.register(perspective);
321        }
322    }
323
324    private void registerEngineExtension(Extension extension) {
325        Object[] contribs = extension.getContributions();
326        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
327        for (Object contrib : contribs) {
328            EngineType engine = (EngineType) contrib;
329            if (!engine.getName().matches("[a-z0-9_\\-]+")) {
330                log.error("Rendering engine names may only contain lowercase alphanumeric characters, underscores and hyphens ");
331                continue;
332            }
333            typeRegistry.register(engine);
334        }
335    }
336
337    private void registerTemplateEngineExtension(Extension extension) {
338        Object[] contribs = extension.getContributions();
339        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
340        for (Object contrib : contribs) {
341            TemplateEngineType engine = (TemplateEngineType) contrib;
342            if (!engine.getName().matches("[a-z0-9_\\-]+")) {
343                log.error("Template engine names may only contain lowercase alphanumeric characters, underscores and hyphens ");
344                continue;
345            }
346            typeRegistry.register(engine);
347        }
348    }
349
350    private void registerThemeExtension(Extension extension) {
351        Object[] contribs = extension.getContributions();
352        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
353
354        for (Object contrib : contribs) {
355            ThemeDescriptor themeDescriptor = (ThemeDescriptor) contrib;
356            themeDescriptor.setContext(extension.getContext());
357            themeDescriptor.setConfigured(true);
358
359            // register the theme descriptor even if the theme fails to load
360            typeRegistry.register(themeDescriptor);
361        }
362    }
363
364    private void registerTheme(ThemeDescriptor themeDescriptor) {
365        String src = themeDescriptor.getSrc();
366        if (src == null) {
367            themeDescriptor.setLoadingFailed(true);
368            log.error("Could not load theme, source not set. ");
369            return;
370        }
371        try {
372            final boolean preload = true;
373            ThemeParser.registerTheme(themeDescriptor, preload);
374        } catch (ThemeIOException e) {
375            log.error("Could not register theme: " + src + " " + e.getMessage());
376        }
377    }
378
379    private void registerThemeSetExtension(Extension extension) {
380        Object[] contribs = extension.getContributions();
381        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
382
383        for (Object contrib : contribs) {
384            ThemeSet themeSet = (ThemeSet) contrib;
385            String name = themeSet.getName();
386
387            ThemeSet oldThemeSet = (ThemeSet) typeRegistry.lookup(TypeFamily.THEMESET, name);
388
389            if (oldThemeSet == null) {
390                typeRegistry.register(themeSet);
391            } else {
392                for (ThemeSetEntry theme : themeSet.getThemes()) {
393                    String themeName = theme.getName();
394                    ThemeSetEntry oldTheme = oldThemeSet.getTheme(themeName);
395                    if (oldTheme == null) {
396                        oldTheme = new ThemeSetEntry(themeName);
397                        oldThemeSet.setTheme(oldTheme);
398                    }
399                    for (String feature : theme.getFeatures()) {
400                        oldThemeSet.addFeatureToTheme(themeName, feature);
401                    }
402                }
403
404            }
405        }
406    }
407
408    private void registerCustomThemes() {
409        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
410        for (File file : ThemeManager.getCustomThemeFiles()) {
411            ThemeDescriptor themeDescriptor = new ThemeDescriptor();
412            themeDescriptor.setConfigured(false);
413
414            String src = null;
415            try {
416                src = String.format("file://%s", file.getCanonicalPath());
417            } catch (IOException e) {
418                themeDescriptor.setLoadingFailed(true);
419                log.error("Could not read theme file: " + src);
420                continue;
421            }
422
423            themeDescriptor.setSrc(src);
424
425            try {
426                final boolean preload = true;
427                ThemeParser.registerTheme(themeDescriptor, preload);
428            } catch (ThemeIOException e) {
429                log.error("Could not register theme: " + src + " " + e.getMessage());
430                continue;
431            }
432            typeRegistry.register(themeDescriptor);
433        }
434        log.debug("Registered local themes");
435    }
436
437    private void registerPresetExtension(Extension extension) {
438        Object[] contribs = extension.getContributions();
439        RuntimeContext extensionContext = extension.getContext();
440        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
441        for (Object contrib : contribs) {
442            if (contrib instanceof PaletteType) {
443                registerPalette((PaletteType) contrib, extensionContext);
444            } else {
445                typeRegistry.register((Type) contrib);
446            }
447        }
448    }
449
450    private void registerPalette(PaletteType palette, RuntimeContext extensionContext) {
451        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
452        String paletteName = palette.getName();
453        String src = palette.getSrc();
454        String category = palette.getCategory();
455        URL url = null;
456        try {
457            url = new URL(src);
458        } catch (MalformedURLException e) {
459            url = extensionContext.getLocalResource(src);
460            if (url == null) {
461                url = extensionContext.getResource(src);
462            }
463        }
464
465        if (url != null) {
466            typeRegistry.register(palette);
467            Map<String, String> entries = PaletteParser.parse(url);
468            for (Map.Entry<String, String> entry : entries.entrySet()) {
469                PresetType preset = new PresetType(entry.getKey(), entry.getValue(), paletteName, category, "", "");
470                typeRegistry.register(preset);
471            }
472        }
473    }
474
475    private void registerViewExtension(Extension extension) {
476        Object[] contribs = extension.getContributions();
477        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
478        for (Object contrib : contribs) {
479            ViewType viewType = (ViewType) contrib;
480            String templateEngineName = viewType.getTemplateEngine();
481            final String viewName = viewType.getViewName();
482            final String viewTypeName = viewType.getTypeName();
483            if (templateEngineName == null) {
484                final String defaultTemplateEngineName = ThemeManager.getDefaultTemplateEngineName();
485                templateEngineName = defaultTemplateEngineName;
486                log.warn(String.format(
487                        "Please set the 'template-engine' attribute on <view name=\"%s\" template-engine=\"...\"> (using default '%s')",
488                        viewName, defaultTemplateEngineName));
489            } else {
490                if (templateEngineName.contains(",")) {
491                    log.warn(String.format(
492                            "The 'template-engine' attribute in <view name=\"%s\" template-engine=\"...\"> may only contain a single template-engine name",
493                            viewName, templateEngineName));
494                    templateEngineName = templateEngineName.split(",")[0];
495                }
496
497                if (viewType.isMerge()) {
498                    final ViewType oldViewType = (ViewType) typeRegistry.lookup(TypeFamily.VIEW, viewTypeName);
499                    if (oldViewType != null) {
500                        // merge resource properties
501                        List<String> newResources = viewType.getResources();
502                        if (!newResources.isEmpty()) {
503                            log.debug("Added resources " + newResources + " to THEME view: " + viewTypeName);
504                            for (String resource : newResources) {
505                                oldViewType.addResource(resource);
506                            }
507                        }
508                    }
509                } else {
510                    typeRegistry.register(viewType);
511                }
512            }
513        }
514    }
515
516    private void unregisterViewExtension(Extension extension) {
517        Object[] contribs = extension.getContributions();
518        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
519        if (typeRegistry != null) {
520            for (Object contrib : contribs) {
521                typeRegistry.unregister((ViewType) contrib);
522            }
523        }
524    }
525
526    private void registerModelExtension(Extension extension) {
527        Object[] contribs = extension.getContributions();
528        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
529        ThemeManager themeManager = (ThemeManager) getRegistry("themes");
530        for (Object contrib : contribs) {
531            ModelType modelType = (ModelType) contrib;
532            final String modelTypeName = modelType.getTypeName();
533            final ModelType oldModelType = (ModelType) typeRegistry.lookup(TypeFamily.MODEL, modelTypeName);
534            if (oldModelType != null) {
535                if (oldModelType.getClassName().equals(modelType.getClassName())) {
536                    log.debug("Model type '" + modelTypeName + "' (" + oldModelType.getClassName()
537                            + ") has already been registered.");
538                } else {
539                    log.warn("Failed to reregister model type '" + modelTypeName + "' (" + oldModelType.getClassName()
540                            + "). The new class " + modelType.getClassName() + " will be ignored.");
541                }
542                continue;
543            }
544            typeRegistry.register(modelType);
545            themeManager.registerModelByClassname(modelType);
546        }
547    }
548
549    private void unregisterModelExtension(Extension extension) {
550        Object[] contribs = extension.getContributions();
551        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
552        ThemeManager themeManager = (ThemeManager) getRegistry("themes");
553        if (typeRegistry != null) {
554            for (Object contrib : contribs) {
555                ModelType modelType = (ModelType) contrib;
556                themeManager.unregisterModelByClassname(modelType);
557                typeRegistry.unregister(modelType);
558            }
559        }
560    }
561
562    private void registerResourceExtension(Extension extension) {
563        Object[] contribs = extension.getContributions();
564        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
565        RuntimeContext extensionContext = extension.getContext();
566        for (Object contrib : contribs) {
567            if (contrib instanceof ResourceType) {
568                ResourceType resourceType = (ResourceType) contrib;
569                typeRegistry.register(resourceType);
570            } else if (contrib instanceof BankImport) {
571                BankImport bankImport = (BankImport) contrib;
572                String bankName = bankImport.getBankName();
573                String collection = bankImport.getCollection();
574                String srcFilePath = bankImport.getSrcFilePath();
575                URL srcFileUrl = extensionContext.getLocalResource(srcFilePath);
576                if (srcFileUrl == null) {
577                    srcFileUrl = extensionContext.getResource(srcFilePath);
578                }
579                if (srcFileUrl == null) {
580                    log.error("Could not import bank resources: " + srcFilePath + " (resource not found)");
581                    continue;
582                }
583                try {
584                    BankManager.importBankData(bankName, collection, srcFileUrl);
585                } catch (IOException e) {
586                    log.error("Could not import bank resources: " + srcFilePath + " (" + e.getMessage() + ")");
587                }
588            }
589        }
590        ThemeManager themeManager = (ThemeManager) getRegistry("themes");
591        themeManager.updateResourceOrdering();
592    }
593
594    private void unregisterResourceExtension(Extension extension) {
595        Object[] contribs = extension.getContributions();
596        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
597        ThemeManager themeManager = (ThemeManager) getRegistry("themes");
598        if (typeRegistry == null || themeManager == null) {
599            return;
600        }
601        RuntimeContext extensionContext = extension.getContext();
602        for (Object contrib : contribs) {
603            if (contrib instanceof ResourceType) {
604                ResourceType resourceType = (ResourceType) contrib;
605                typeRegistry.unregister(resourceType);
606                themeManager.unregisterResourceOrdering(resourceType);
607            } else if (contrib instanceof BankImport) {
608                BankImport bankImport = (BankImport) contrib;
609                String bankName = bankImport.getBankName();
610                String srcFilePath = bankImport.getSrcFilePath();
611                // TODO
612            }
613        }
614    }
615
616    private void registerBank(Extension extension) {
617        Object[] contribs = extension.getContributions();
618        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
619        for (Object contrib : contribs) {
620            if (contrib instanceof ResourceBank) {
621                ResourceBank resourceBank = (ResourceBank) contrib;
622                typeRegistry.register(resourceBank);
623            }
624        }
625    }
626
627    private void unregisterBank(Extension extension) {
628        Object[] contribs = extension.getContributions();
629        TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types");
630        if (typeRegistry == null) {
631            return;
632        }
633        for (Object contrib : contribs) {
634            if (contrib instanceof ResourceBank) {
635                ResourceBank resourceBank = (ResourceBank) contrib;
636                typeRegistry.unregister(resourceBank);
637            }
638        }
639    }
640
641}