@Override
  protected void onBootstrap(ApplicationEvent event) {
    // Reset the dictionary (destroy and reload)
    dictionaryDAO.reset();

    // Register listeners
    register();

    // The listeners can now know about this
    ((ApplicationContext) event.getSource())
        .publishEvent(new DictionaryRepositoryBootstrappedEvent(this));
  }
  /** Register listeners */
  public void register() {
    // register with Dictionary Service to allow (re-)init
    dictionaryDAO.registerListener(this);

    // register with Message Service to allow (re-)init
    messageService.register(this);

    if (tenantAdminService.isEnabled()) {
      // register dictionary repository bootstrap
      tenantAdminService.register(this);

      // register repository message (I18N) service
      tenantAdminService.register(messageService);
    }
  }
  /**
   * Loads a model (and its dependents) if it does not exist in the list of loaded models.
   *
   * @param modelMap a map of the models to be loaded
   * @param loadedModels the list of models already loaded
   * @param model the model to try and load
   */
  private void loadModel(
      Map<String, DynamicModelInfo> modelMap,
      List<String> loadedModels,
      M2Model model,
      RepositoryLocation modelLocation) {
    String modelName = model.getName();
    if (loadedModels.contains(modelName) == false) {
      for (M2Namespace importNamespace : model.getImports()) {
        DynamicModelInfo entry = modelMap.get(importNamespace.getUri());
        if (entry != null) {
          RepositoryLocation importedLocation = entry.location;
          M2Model importedModel = entry.model;

          // Ensure that the imported model is loaded first
          loadModel(modelMap, loadedModels, importedModel, importedLocation);
        }
        // else we can assume that the imported model is already loaded, if this not the case then
        //      an error will be raised during compilation
      }

      try {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Loading model: "
                  + modelName
                  + " (from ["
                  + modelLocation.getStoreRef()
                  + "]"
                  + modelLocation.getPath()
                  + ")");
        }

        dictionaryDAO.putModel(model);

        loadedModels.add(modelName);
      } catch (AlfrescoRuntimeException e) {
        // note: skip with warning - to allow server to start, and hence allow the possibility of
        // fixing the broken model(s)
        logger.warn("Failed to load model '" + modelName + "' : " + e);
      }
    }
  }
  /** Perform the actual repository access, checking for the existence of a valid transaction */
  private void onDictionaryInitInTxn() {
    if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_NONE) {
      throw new IllegalStateException(
          "The Repository-based dictionary initialization has to be done in the context of a transaction.");
    }

    long startTime = System.currentTimeMillis();

    if (logger.isTraceEnabled()) {
      String tenantDomain = tenantAdminService.getCurrentUserDomain();
      logger.trace(
          "onDictionaryInit: ["
              + Thread.currentThread()
              + "]"
              + (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)
                  ? ""
                  : " (Tenant: " + tenantDomain + ")"));
    }

    Collection<QName> modelsBefore = dictionaryDAO.getModels(true); // note: re-entrant
    int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);

    List<String> loadedModels = new ArrayList<String>();

    if (this.repositoryModelsLocations != null) {
      // URI to model map
      Map<String, DynamicModelInfo> modelMap = new HashMap<String, DynamicModelInfo>();

      if (logger.isTraceEnabled()) {
        logger.trace("onDictionaryInit: locations=" + this.repositoryModelsLocations);
      }

      // Register the models found in the repository

      for (RepositoryLocation repositoryLocation : this.repositoryModelsLocations) {
        StoreRef storeRef = repositoryLocation.getStoreRef();

        if (!nodeService.exists(storeRef)) {
          logger.info("StoreRef '" + storeRef + "' does not exist");
          continue; // skip this location
        }

        List<NodeRef> nodeRefs = null;

        if (repositoryLocation.getQueryLanguage().equals(RepositoryLocation.LANGUAGE_PATH)) {
          nodeRefs = getNodes(storeRef, repositoryLocation, ContentModel.TYPE_DICTIONARY_MODEL);

          if (nodeRefs.size() > 0) {
            for (NodeRef dictionaryModel : nodeRefs) {
              try {
                // Ignore if the node is a working copy or archived, or if its inactive
                if (!(nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_WORKING_COPY)
                    || nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_ARCHIVED))) {
                  Boolean isActive =
                      (Boolean)
                          nodeService.getProperty(dictionaryModel, ContentModel.PROP_MODEL_ACTIVE);

                  if ((isActive != null) && (isActive.booleanValue() == true)) {
                    M2Model model = createM2Model(dictionaryModel);
                    if (model != null) {
                      if (logger.isTraceEnabled()) {
                        logger.trace(
                            "onDictionaryInit: " + model.getName() + " (" + dictionaryModel + ")");
                      }

                      for (M2Namespace namespace : model.getNamespaces()) {
                        modelMap.put(
                            namespace.getUri(),
                            new DynamicModelInfo(repositoryLocation, model, dictionaryModel));
                      }
                    }
                  }
                }
              } catch (InvalidNodeRefException inre) {
                // ignore - model no longer exists
                if (logger.isDebugEnabled()) {
                  logger.debug("onDictionaryInit: " + inre + " (assume concurrently deleted)");
                }

                continue;
              }
            }
          }
        } else {
          logger.error(
              "Unsupported query language for models location: "
                  + repositoryLocation.getQueryLanguage());
        }
      }

      // Load the models ensuring that they are loaded in the correct order
      for (Map.Entry<String, DynamicModelInfo> entry : modelMap.entrySet()) {
        RepositoryLocation importedLocation = entry.getValue().location;
        M2Model importedModel = entry.getValue().model;
        loadModel(modelMap, loadedModels, importedModel, importedLocation);
        notifyDynamicModelLoaded(entry.getValue());
      }
    }

    Collection<QName> modelsAfter = dictionaryDAO.getModels(true);
    int modelsAfterCnt = (modelsAfter != null ? modelsAfter.size() : 0);

    if (logger.isDebugEnabled()) {
      String tenantDomain = tenantAdminService.getCurrentUserDomain();
      logger.debug(
          "Model count: before="
              + modelsBeforeCnt
              + ", load/update="
              + loadedModels.size()
              + ", after="
              + modelsAfterCnt
              + " in "
              + (System.currentTimeMillis() - startTime)
              + " msecs ["
              + Thread.currentThread()
              + "] "
              + (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)
                  ? ""
                  : " (Tenant: " + tenantDomain + ")"));
    }
  }