/**
   * Returns the security token required to bind to a naming context.
   *
   * @param context the catalina context
   * @return the security token for use with <code>ContextBindings</code>
   */
  protected Object getNamingToken(Context context) {
    // null token worked before 8.0.6
    Object token = null;
    if (!ContextAccessController.checkSecurityToken(context, token)) {
      // namingToken added to Context and Server interfaces in 8.0.6
      // Used by NamingContextListener when settinp up JNDI context
      token = context.getNamingToken();
      if (!ContextAccessController.checkSecurityToken(context, token)) {
        logger.error("Couldn't get a valid security token. ClassLoader binding will fail.");
      }
    }

    return token;
  }
  /**
   * Process property change events. Currently, only listens to such events on the <code>
   * NamingResources</code> instance for the global naming resources.
   *
   * @param event The property change event that has occurred
   */
  public void propertyChange(PropertyChangeEvent event) {

    if (!initialized) return;

    Object source = event.getSource();
    if (source == namingResources) {

      // Setting the context in read/write mode
      ContextAccessController.setWritable(getName(), container);

      processGlobalResourcesChange(
          event.getPropertyName(), event.getOldValue(), event.getNewValue());

      // Setting the context in read only mode
      ContextAccessController.setReadOnly(getName());
    }
  }
  /**
   * Acknowledge the occurrence of the specified event. Note: Will never be called when the listener
   * is associated to a Server, since it is not a Container.
   *
   * @param event ContainerEvent that has occurred
   */
  @Override
  public void containerEvent(ContainerEvent event) {

    if (!initialized) return;

    // Setting the context in read/write mode
    ContextAccessController.setWritable(getName(), token);

    String type = event.getType();

    if (type.equals("addEjb")) {

      String ejbName = (String) event.getData();
      if (ejbName != null) {
        ContextEjb ejb = namingResources.findEjb(ejbName);
        addEjb(ejb);
      }

    } else if (type.equals("addEnvironment")) {

      String environmentName = (String) event.getData();
      if (environmentName != null) {
        ContextEnvironment env = namingResources.findEnvironment(environmentName);
        addEnvironment(env);
      }

    } else if (type.equals("addLocalEjb")) {

      String localEjbName = (String) event.getData();
      if (localEjbName != null) {
        ContextLocalEjb localEjb = namingResources.findLocalEjb(localEjbName);
        addLocalEjb(localEjb);
      }

    } else if (type.equals("addResource")) {

      String resourceName = (String) event.getData();
      if (resourceName != null) {
        ContextResource resource = namingResources.findResource(resourceName);
        addResource(resource);
      }

    } else if (type.equals("addResourceLink")) {

      String resourceLinkName = (String) event.getData();
      if (resourceLinkName != null) {
        ContextResourceLink resourceLink = namingResources.findResourceLink(resourceLinkName);
        addResourceLink(resourceLink);
      }

    } else if (type.equals("addResourceEnvRef")) {

      String resourceEnvRefName = (String) event.getData();
      if (resourceEnvRefName != null) {
        ContextResourceEnvRef resourceEnvRef =
            namingResources.findResourceEnvRef(resourceEnvRefName);
        addResourceEnvRef(resourceEnvRef);
      }

    } else if (type.equals("addService")) {

      String serviceName = (String) event.getData();
      if (serviceName != null) {
        ContextService service = namingResources.findService(serviceName);
        addService(service);
      }

    } else if (type.equals("removeEjb")) {

      String ejbName = (String) event.getData();
      if (ejbName != null) {
        removeEjb(ejbName);
      }

    } else if (type.equals("removeEnvironment")) {

      String environmentName = (String) event.getData();
      if (environmentName != null) {
        removeEnvironment(environmentName);
      }

    } else if (type.equals("removeLocalEjb")) {

      String localEjbName = (String) event.getData();
      if (localEjbName != null) {
        removeLocalEjb(localEjbName);
      }

    } else if (type.equals("removeResource")) {

      String resourceName = (String) event.getData();
      if (resourceName != null) {
        removeResource(resourceName);
      }

    } else if (type.equals("removeResourceLink")) {

      String resourceLinkName = (String) event.getData();
      if (resourceLinkName != null) {
        removeResourceLink(resourceLinkName);
      }

    } else if (type.equals("removeResourceEnvRef")) {

      String resourceEnvRefName = (String) event.getData();
      if (resourceEnvRefName != null) {
        removeResourceEnvRef(resourceEnvRefName);
      }

    } else if (type.equals("removeService")) {

      String serviceName = (String) event.getData();
      if (serviceName != null) {
        removeService(serviceName);
      }
    }

    // Setting the context in read only mode
    ContextAccessController.setReadOnly(getName());
  }
  /**
   * Acknowledge the occurrence of the specified event.
   *
   * @param event LifecycleEvent that has occurred
   */
  @Override
  public void lifecycleEvent(LifecycleEvent event) {

    container = event.getLifecycle();

    if (container instanceof Context) {
      namingResources = ((Context) container).getNamingResources();
      logger = log;
      token = ((Context) container).getNamingToken();
    } else if (container instanceof Server) {
      namingResources = ((Server) container).getGlobalNamingResources();
      token = ((Server) container).getNamingToken();
    } else {
      return;
    }

    if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {

      if (initialized) return;

      try {
        Hashtable<String, Object> contextEnv = new Hashtable<>();
        namingContext = new NamingContext(contextEnv, getName());
        ContextAccessController.setSecurityToken(getName(), token);
        ContextAccessController.setSecurityToken(container, token);
        ContextBindings.bindContext(container, namingContext, token);
        if (log.isDebugEnabled()) {
          log.debug("Bound " + container);
        }

        // Configure write when read-only behaviour
        namingContext.setExceptionOnFailedWrite(getExceptionOnFailedWrite());

        // Setting the context in read/write mode
        ContextAccessController.setWritable(getName(), token);

        try {
          createNamingContext();
        } catch (NamingException e) {
          logger.error(sm.getString("naming.namingContextCreationFailed", e));
        }

        namingResources.addPropertyChangeListener(this);

        // Binding the naming context to the class loader
        if (container instanceof Context) {
          // Setting the context in read only mode
          ContextAccessController.setReadOnly(getName());
          try {
            ContextBindings.bindClassLoader(
                container, token, ((Context) container).getLoader().getClassLoader());
          } catch (NamingException e) {
            logger.error(sm.getString("naming.bindFailed", e));
          }
        }

        if (container instanceof Server) {
          org.apache.naming.factory.ResourceLinkFactory.setGlobalContext(namingContext);
          try {
            ContextBindings.bindClassLoader(container, token, this.getClass().getClassLoader());
          } catch (NamingException e) {
            logger.error(sm.getString("naming.bindFailed", e));
          }
          if (container instanceof StandardServer) {
            ((StandardServer) container).setGlobalNamingContext(namingContext);
          }
        }

      } finally {
        // Regardless of success, so that we can do cleanup on configure_stop
        initialized = true;
      }

    } else if (Lifecycle.CONFIGURE_STOP_EVENT.equals(event.getType())) {

      if (!initialized) return;

      try {
        // Setting the context in read/write mode
        ContextAccessController.setWritable(getName(), token);
        ContextBindings.unbindContext(container, token);

        if (container instanceof Context) {
          ContextBindings.unbindClassLoader(
              container, token, ((Context) container).getLoader().getClassLoader());
        }

        if (container instanceof Server) {
          namingResources.removePropertyChangeListener(this);
          ContextBindings.unbindClassLoader(container, token, this.getClass().getClassLoader());
        }

        ContextAccessController.unsetSecurityToken(getName(), token);
        ContextAccessController.unsetSecurityToken(container, token);

        // unregister mbeans.
        if (!objectNames.isEmpty()) {
          Collection<ObjectName> names = objectNames.values();
          Registry registry = Registry.getRegistry(null, null);
          for (ObjectName objectName : names) {
            registry.unregisterComponent(objectName);
          }
        }
      } finally {
        objectNames.clear();

        namingContext = null;
        envCtx = null;
        compCtx = null;
        initialized = false;
      }
    }
  }
  /**
   * Acknowledge the occurrence of the specified event.
   *
   * @param event LifecycleEvent that has occurred
   */
  public void lifecycleEvent(LifecycleEvent event) {

    container = event.getLifecycle();

    if (container instanceof Context) {
      namingResources = ((Context) container).getNamingResources();
    } else if (container instanceof Server) {
      namingResources = ((Server) container).getGlobalNamingResources();
    } else {
      return;
    }

    if (event.getType() == Lifecycle.START_EVENT) {

      if (initialized) return;

      Hashtable contextEnv = new Hashtable();
      try {
        namingContext = new NamingContext(contextEnv, getName());
      } catch (NamingException e) {
        // Never happens
      }
      ContextAccessController.setSecurityToken(getName(), container);
      ContextBindings.bindContext(container, namingContext, container);

      // Setting the context in read/write mode
      ContextAccessController.setWritable(getName(), container);

      try {
        createNamingContext();
      } catch (NamingException e) {
        log(sm.getString("naming.namingContextCreationFailed", e));
      }

      // Binding the naming context to the class loader
      if (container instanceof Context) {
        // Setting the context in read only mode
        ContextAccessController.setReadOnly(getName());
        try {
          ContextBindings.bindClassLoader(
              container, container, ((Container) container).getLoader().getClassLoader());
        } catch (NamingException e) {
          log(sm.getString("naming.bindFailed", e));
        }
      }

      if (container instanceof Server) {
        namingResources.addPropertyChangeListener(this);
        org.apache.naming.factory.ResourceLinkFactory.setGlobalContext(namingContext);
        try {
          ContextBindings.bindClassLoader(container, container, this.getClass().getClassLoader());
        } catch (NamingException e) {
          log(sm.getString("naming.bindFailed", e));
        }
        if (container instanceof StandardServer) {
          ((StandardServer) container).setGlobalNamingContext(namingContext);
        }
      }

      initialized = true;

    } else if (event.getType() == Lifecycle.STOP_EVENT) {

      if (!initialized) return;

      // Setting the context in read/write mode
      ContextAccessController.setWritable(getName(), container);

      if (container instanceof Context) {
        ContextBindings.unbindClassLoader(
            container, container, ((Container) container).getLoader().getClassLoader());
      }

      if (container instanceof Server) {
        namingResources.removePropertyChangeListener(this);
        ContextBindings.unbindClassLoader(container, container, this.getClass().getClassLoader());
      }

      ContextAccessController.unsetSecurityToken(getName(), container);

      namingContext = null;
      envCtx = null;
      compCtx = null;
      initialized = false;
    }
  }