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}