Beispiel #1
0
  public static Object build(
      final Collection<ServiceInfo> services,
      final ServiceInfo info,
      final ObjectRecipe serviceRecipe) {
    if ("org.apache.openejb.config.sys.MapFactory".equals(info.className)) {
      return info.properties;
    }

    if (!info.properties.containsKey("properties")) {
      info.properties.put("properties", new UnsetPropertiesRecipe());
    }

    // we can't ask for having a setter for existing code
    serviceRecipe.allow(Option.FIELD_INJECTION);
    serviceRecipe.allow(Option.PRIVATE_PROPERTIES);

    for (final Map.Entry<Object, Object> entry : info.properties.entrySet()) { // manage links
      final String key = entry.getKey().toString();
      final Object value = entry.getValue();
      if (value instanceof String) {
        final String valueStr = value.toString();
        if (valueStr.startsWith("$")) {
          serviceRecipe.setProperty(key, resolve(services, valueStr.substring(1)));
        } else if (valueStr.startsWith("@")) {
          final Context jndiContext =
              SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext();
          try {
            serviceRecipe.setProperty(
                key,
                jndiContext.lookup(
                    JndiConstants.OPENEJB_RESOURCE_JNDI_PREFIX + valueStr.substring(1)));
          } catch (final NamingException e) {
            try {
              serviceRecipe.setProperty(key, jndiContext.lookup(valueStr.substring(1)));
            } catch (final NamingException e1) {
              Logger.getInstance(LogCategory.OPENEJB, ServiceInfos.class)
                  .warning(
                      "Value "
                          + valueStr
                          + " starting with @ but doesn't point to an existing resource, using raw value");
              serviceRecipe.setProperty(key, value);
            }
          }
        } else {
          serviceRecipe.setProperty(key, value);
        }
      } else {
        serviceRecipe.setProperty(key, entry.getValue());
      }
    }

    final Object service = serviceRecipe.create();

    SystemInstance.get()
        .addObserver(
            service); // TODO: remove it? in all case the observer should remove itself when done
    Assembler.logUnusedProperties(serviceRecipe, info);

    return service;
  }
Beispiel #2
0
public class DynamicMBeanWrapper implements DynamicMBean, MBeanRegistration {
  public static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB_DEPLOY, DynamicMBeanWrapper.class);

  private static final Map<Class<?>, CacheInfo> CACHE = new HashMap<Class<?>, CacheInfo>();

  private static final Map<Class<?>, Class<? extends Annotation>> OPENEJB_API_TO_JAVAX =
      new HashMap<Class<?>, Class<? extends Annotation>>();

  static {
    final ClassLoader loader = DynamicMBeanWrapper.class.getClassLoader();
    try {
      OPENEJB_API_TO_JAVAX.put(
          MBean.class, (Class<? extends Annotation>) loader.loadClass("javax.management.MBean"));
      OPENEJB_API_TO_JAVAX.put(
          Description.class,
          (Class<? extends Annotation>) loader.loadClass("javax.management.Description"));
      OPENEJB_API_TO_JAVAX.put(
          ManagedOperation.class,
          (Class<? extends Annotation>) loader.loadClass("javax.management.ManagedOperation"));
      OPENEJB_API_TO_JAVAX.put(
          ManagedAttribute.class,
          (Class<? extends Annotation>) loader.loadClass("javax.management.ManagedAttribute"));
      OPENEJB_API_TO_JAVAX.put(
          NotificationInfo.class,
          (Class<? extends Annotation>) loader.loadClass("javax.management.NotificationInfo"));
      OPENEJB_API_TO_JAVAX.put(
          NotificationInfos.class,
          (Class<? extends Annotation>) loader.loadClass("javax.management.NotificationInfos"));
    } catch (final ClassNotFoundException | NoClassDefFoundError cnfe) {
      // ignored
    }
  }

  private final MBeanInfo info;
  private final Map<String, Method> getters = new HashMap<String, Method>();
  private final Map<String, Method> setters = new HashMap<String, Method>();
  private final Map<String, Method> operations = new HashMap<String, Method>();
  private final Object instance;
  private final ClassLoader classloader;

  public DynamicMBeanWrapper(final Object givenInstance) {
    this(null, givenInstance);
  }

  public DynamicMBeanWrapper(final WebBeansContext wc, final Object givenInstance) {
    Class<?> annotatedMBean = givenInstance.getClass();

    // javaassist looses annotation so simply unwrap it
    if (wc != null) {
      if (givenInstance.getClass().getName().contains("$Owb")) { // isProxy
        annotatedMBean = annotatedMBean.getSuperclass();
      }
    }

    classloader = annotatedMBean.getClassLoader();
    instance = givenInstance;

    final CacheInfo cache = CACHE.get(annotatedMBean);
    if (cache == null) {
      final String description;
      final List<MBeanAttributeInfo> attributeInfos = new ArrayList<MBeanAttributeInfo>();
      final List<MBeanOperationInfo> operationInfos = new ArrayList<MBeanOperationInfo>();
      final List<MBeanNotificationInfo> notificationInfos = new ArrayList<MBeanNotificationInfo>();

      // class
      final Description classDescription = findAnnotation(annotatedMBean, Description.class);
      description = getDescription(classDescription, "a MBean built by OpenEJB");

      final NotificationInfo notification = findAnnotation(annotatedMBean, NotificationInfo.class);
      if (notification != null) {
        final MBeanNotificationInfo notificationInfo = getNotificationInfo(notification);
        notificationInfos.add(notificationInfo);
      }

      final NotificationInfos notifications =
          findAnnotation(annotatedMBean, NotificationInfos.class);
      if (notifications != null && notifications.value() != null) {
        for (final NotificationInfo n : notifications.value()) {
          final MBeanNotificationInfo notificationInfo = getNotificationInfo(n);
          notificationInfos.add(notificationInfo);
        }
      }

      // methods
      for (final Method m : annotatedMBean.getMethods()) {
        final int modifiers = m.getModifiers();
        if (m.getDeclaringClass().equals(Object.class)
            || !Modifier.isPublic(modifiers)
            || Modifier.isAbstract(modifiers)) {
          continue;
        }

        if (findAnnotation(m, ManagedAttribute.class) != null) {
          final String methodName = m.getName();
          String attrName = methodName;
          if ((attrName.startsWith("get") && m.getParameterTypes().length == 0
                  || attrName.startsWith("set") && m.getParameterTypes().length == 1)
              && attrName.length() > 3) {
            attrName = attrName.substring(3);
            if (attrName.length() > 1) {
              attrName = Character.toLowerCase(attrName.charAt(0)) + attrName.substring(1);
            } else {
              attrName = attrName.toLowerCase();
            }
          } else {
            logger.warning(
                "ignoring attribute " + m.getName() + " for " + annotatedMBean.getName());
          }

          if (methodName.startsWith("get")) {
            getters.put(attrName, m);
          } else if (methodName.startsWith("set")) {
            setters.put(attrName, m);
          }
        } else if (findAnnotation(m, ManagedOperation.class) != null) {
          operations.put(m.getName(), m);

          String operationDescr = "";
          final Description descr = findAnnotation(m, Description.class);
          if (descr != null) {
            operationDescr = getDescription(descr, "-");
          }

          operationInfos.add(newMethodDescriptor(operationDescr, m));
        }
      }

      for (final Map.Entry<String, Method> e : getters.entrySet()) {
        final String key = e.getKey();
        final Method mtd = e.getValue();

        String attrDescr = "";
        final Description descr = findAnnotation(mtd, Description.class);
        if (descr != null) {
          attrDescr = getDescription(descr, "-");
        }

        try {
          attributeInfos.add(new MBeanAttributeInfo(key, attrDescr, mtd, setters.get(key)));
        } catch (final IntrospectionException ex) {
          logger.warning("can't manage " + key + " for " + mtd.getName(), ex);
        }
      }

      // for updatable but not readable attributes
      for (final Map.Entry<String, Method> e : setters.entrySet()) {
        final String key = e.getKey();
        if (getters.get(key) != null) {
          continue; // already done
        }

        final Method mtd = e.getValue();

        String attrDescr = "";
        final Description descr = findAnnotation(mtd, Description.class);
        if (descr != null) {
          attrDescr = getDescription(descr, "-");
        }

        try {
          attributeInfos.add(new MBeanAttributeInfo(key, attrDescr, null, setters.get(key)));
        } catch (final IntrospectionException ex) {
          logger.warning("can't manage " + key + " for " + mtd.getName(), ex);
        }
      }

      info =
          new MBeanInfo(
              annotatedMBean.getName(),
              description,
              attributeInfos.toArray(new MBeanAttributeInfo[attributeInfos.size()]),
              null, // default constructor is mandatory
              operationInfos.toArray(new MBeanOperationInfo[operationInfos.size()]),
              notificationInfos.toArray(new MBeanNotificationInfo[notificationInfos.size()]));

      if (annotatedMBean.getAnnotation(Internal.class) != null) {
        CACHE.put(annotatedMBean, new CacheInfo(info, getters, setters, operations));
      }
    } else {
      info = cache.mBeanInfo;
      getters.putAll(cache.getters);
      setters.putAll(cache.setters);
      operations.putAll(cache.operations);
    }
  }

  private MBeanOperationInfo newMethodDescriptor(final String operationDescr, final Method m) {
    final MBeanOperationInfo jvmInfo = new MBeanOperationInfo(operationDescr, m);
    return new MBeanOperationInfo(
        m.getName(),
        operationDescr,
        methodSignature(jvmInfo, m),
        m.getReturnType().getName(),
        MBeanOperationInfo.UNKNOWN,
        jvmInfo.getDescriptor()); // avoid to copy the logic
  }

  private static MBeanParameterInfo[] methodSignature(
      final MBeanOperationInfo jvmInfo, final Method method) {
    final Class<?>[] classes = method.getParameterTypes();
    final Annotation[][] annots = method.getParameterAnnotations();
    return parameters(jvmInfo, classes, annots);
  }

  static MBeanParameterInfo[] parameters(
      final MBeanOperationInfo jvmInfo, final Class<?>[] classes, final Annotation[][] annots) {
    final MBeanParameterInfo[] params = new MBeanParameterInfo[classes.length];
    assert classes.length == annots.length;

    String desc = "";
    for (int i = 0; i < classes.length; i++) {
      final Descriptor d = jvmInfo.getSignature()[i].getDescriptor();
      final String pn = "arg" + i;
      for (final Annotation a : annots[i]) {
        final Class<? extends Annotation> type = a.annotationType();
        if (type.equals(Description.class)
            || type.equals(OPENEJB_API_TO_JAVAX.get(Description.class))) {
          desc = getDescription(annotationProxy(a, Description.class), desc);
          break;
        }
      }
      params[i] = new MBeanParameterInfo(pn, classes[i].getName(), desc, d);
    }

    return params;
  }

  private <T extends Annotation> T findAnnotation(
      final Method method, final Class<T> searchedAnnotation) {
    final T annotation = method.getAnnotation(searchedAnnotation);
    if (annotation != null) {
      return annotation;
    }

    if (OPENEJB_API_TO_JAVAX.containsKey(searchedAnnotation)) {
      final Class<? extends Annotation> clazz = OPENEJB_API_TO_JAVAX.get(searchedAnnotation);
      final Object javaxAnnotation = method.getAnnotation(clazz);
      if (javaxAnnotation != null) {
        return annotationProxy(javaxAnnotation, searchedAnnotation);
      }
    }
    return null;
  }

  private <T extends Annotation> T findAnnotation(
      final Class<?> annotatedMBean, final Class<T> searchedAnnotation) {
    final T annotation = annotatedMBean.getAnnotation(searchedAnnotation);
    if (annotation != null) {
      return annotation;
    }

    if (OPENEJB_API_TO_JAVAX.containsKey(searchedAnnotation)) {
      final Class<? extends Annotation> clazz = OPENEJB_API_TO_JAVAX.get(searchedAnnotation);
      final Object javaxAnnotation = annotatedMBean.getAnnotation(clazz);
      if (javaxAnnotation != null) {
        return annotationProxy(javaxAnnotation, searchedAnnotation);
      }
    }
    return null;
  }

  private static <T extends Annotation> T annotationProxy(
      final Object javaxAnnotation, final Class<T> clazz) {
    return (T)
        Proxy.newProxyInstance(
            DynamicMBeanWrapper.class.getClassLoader(),
            new Class<?>[] {clazz},
            new AnnotationHandler(javaxAnnotation));
  }

  private static MBeanNotificationInfo getNotificationInfo(final NotificationInfo n) {
    final String description = getDescription(n.description(), "-");
    return new MBeanNotificationInfo(
        n.types(),
        n.notificationClass().getName(),
        description,
        new ImmutableDescriptor(n.descriptorFields()));
  }

  private static String getDescription(final Description d, final String defaultValue) {
    if (d != null) {
      if (d.bundleBaseName() != null && d.key() != null) {
        try {
          return ResourceBundle.getBundle(d.bundleBaseName()).getString(d.key());
        } catch (final RuntimeException re) {
          return d.value();
        }
      } else {
        return d.value();
      }
    }
    return defaultValue;
  }

  @Override
  public MBeanInfo getMBeanInfo() {
    return info;
  }

  @Override
  public Object getAttribute(final String attribute)
      throws AttributeNotFoundException, MBeanException, ReflectionException {
    if (getters.containsKey(attribute)) {
      final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classloader);
      try {
        return getters.get(attribute).invoke(instance);
      } catch (final IllegalArgumentException
          | InvocationTargetException
          | IllegalAccessException e) {
        logger.error("can't get " + attribute + " value", e);
      } finally {
        Thread.currentThread().setContextClassLoader(oldCl);
      }
    }
    throw new AttributeNotFoundException();
  }

  @Override
  public void setAttribute(final Attribute attribute)
      throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
          ReflectionException {
    if (setters.containsKey(attribute.getName())) {
      final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classloader);
      try {
        setters.get(attribute.getName()).invoke(instance, attribute.getValue());
      } catch (final IllegalArgumentException
          | InvocationTargetException
          | IllegalAccessException e) {
        logger.error("can't set " + attribute + " value", e);
      } finally {
        Thread.currentThread().setContextClassLoader(oldCl);
      }
    } else {
      throw new AttributeNotFoundException();
    }
  }

  @Override
  public AttributeList getAttributes(final String[] attributes) {
    final AttributeList list = new AttributeList();
    for (final String n : attributes) {
      try {
        list.add(new Attribute(n, getAttribute(n)));
      } catch (final Exception ignore) {
        // no-op
      }
    }
    return list;
  }

  @Override
  public AttributeList setAttributes(final AttributeList attributes) {
    final AttributeList list = new AttributeList();
    for (final Object o : attributes) {
      final Attribute attr = (Attribute) o;
      try {
        setAttribute(attr);
        list.add(attr);
      } catch (final Exception ignore) {
        // no-op
      }
    }
    return list;
  }

  @Override
  public Object invoke(final String actionName, final Object[] params, final String[] signature)
      throws MBeanException, ReflectionException {
    if (operations.containsKey(actionName)) {
      final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classloader);
      try {
        return operations.get(actionName).invoke(instance, params);
      } catch (final IllegalArgumentException
          | InvocationTargetException
          | IllegalAccessException e) {
        logger.error(actionName + "can't be invoked", e);
      } finally {
        Thread.currentThread().setContextClassLoader(oldCl);
      }
    }
    throw new MBeanException(new IllegalArgumentException(), actionName + " doesn't exist");
  }

  @Override
  public ObjectName preRegister(final MBeanServer server, final ObjectName name) throws Exception {
    final Thread thread = Thread.currentThread();
    final ClassLoader oldCl = thread.getContextClassLoader();
    thread.setContextClassLoader(classloader);
    try {
      if (MBeanRegistration.class.isInstance(instance)) {
        return MBeanRegistration.class.cast(instance).preRegister(server, name);
      }
      return name;
    } finally {
      thread.setContextClassLoader(oldCl);
    }
  }

  @Override
  public void postRegister(final Boolean registrationDone) {
    final Thread thread = Thread.currentThread();
    final ClassLoader oldCl = thread.getContextClassLoader();
    thread.setContextClassLoader(classloader);
    try {
      if (MBeanRegistration.class.isInstance(instance)) {
        MBeanRegistration.class.cast(instance).postRegister(registrationDone);
      }
    } finally {
      thread.setContextClassLoader(oldCl);
    }
  }

  @Override
  public void preDeregister() throws Exception {
    final Thread thread = Thread.currentThread();
    final ClassLoader oldCl = thread.getContextClassLoader();
    thread.setContextClassLoader(classloader);
    try {
      if (MBeanRegistration.class.isInstance(instance)) {
        MBeanRegistration.class.cast(instance).preDeregister();
      }
    } finally {
      thread.setContextClassLoader(oldCl);
    }
  }

  @Override
  public void postDeregister() {
    final Thread thread = Thread.currentThread();
    final ClassLoader oldCl = thread.getContextClassLoader();
    thread.setContextClassLoader(classloader);
    try {
      if (MBeanRegistration.class.isInstance(instance)) {
        MBeanRegistration.class.cast(instance).postDeregister();
      }
    } finally {
      thread.setContextClassLoader(oldCl);
    }
  }

  private static class AnnotationHandler implements InvocationHandler {
    private final Object delegate;

    public AnnotationHandler(final Object javaxAnnotation) {
      delegate = javaxAnnotation;
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args)
        throws Throwable {
      Object result = null;
      for (final Method mtd :
          delegate.getClass().getMethods()) { // simple heurisitc which should be enough
        if (mtd.getName().equals(method.getName())) {
          result = mtd.invoke(delegate, args);
          break;
        }
      }

      if (result == null) {
        return null;
      }

      if (result.getClass().isArray()) {
        final Object[] array = (Object[]) result;
        if (array.length == 0 || !OPENEJB_API_TO_JAVAX.containsValue(array[0].getClass())) {
          return array;
        }

        final Object[] translated = new Object[array.length];
        for (int i = 0; i < translated.length; i++) {
          translated[i] = annotationProxy(array[i], OPENEJB_API_TO_JAVAX.get(array[i].getClass()));
        }
      }

      return result;
    }
  }

  private static final class CacheInfo {
    public final MBeanInfo mBeanInfo;
    public final Map<String, Method> getters;
    public final Map<String, Method> setters;
    public final Map<String, Method> operations;

    private CacheInfo(
        final MBeanInfo mBeanInfo,
        final Map<String, Method> getters,
        final Map<String, Method> setters,
        final Map<String, Method> operations) {
      this.mBeanInfo = mBeanInfo;
      this.getters = getters;
      this.setters = setters;
      this.operations = operations;
    }
  }
}
public class DeployTimeEnhancer {
  private static final Logger LOGGER =
      Logger.getInstance(LogCategory.OPENEJB_DEPLOY, DeployTimeEnhancer.class);

  private static final String OPENEJB_JAR_ENHANCEMENT_INCLUDE = "openejb.jar.enhancement.include";
  private static final String OPENEJB_JAR_ENHANCEMENT_EXCLUDE = "openejb.jar.enhancement.exclude";

  private static final String CLASS_EXT = ".class";
  private static final String PROPERTIES_FILE_PROP = "propertiesFile";
  private static final String META_INF_PERSISTENCE_XML = "META-INF/persistence.xml";
  private static final String TMP_ENHANCEMENT_SUFFIX = ".tmp-enhancement";

  private final Method enhancerMethod;
  private final Constructor<?> optionsConstructor;

  public DeployTimeEnhancer() {
    Method mtd;
    Constructor<?> cstr;
    final ClassLoader cl = DeployTimeEnhancer.class.getClassLoader();
    try {
      final Class<?> enhancerClass = cl.loadClass("org.apache.openjpa.enhance.PCEnhancer");
      final Class<?> arg2 = cl.loadClass("org.apache.openjpa.lib.util.Options");
      cstr = arg2.getConstructor(Properties.class);
      mtd = enhancerClass.getMethod("run", String[].class, arg2);
    } catch (Exception e) {
      LOGGER.warning("openjpa enhancer can't be found in the container, will be skipped");
      mtd = null;
      cstr = null;
    }
    optionsConstructor = cstr;
    enhancerMethod = mtd;
  }

  public void enhance(@Observes final BeforeDeploymentEvent event) {
    if (enhancerMethod == null) {
      LOGGER.debug("OpenJPA is not available so no deploy-time enhancement will be done");
      return;
    }

    // find persistence.xml
    final Map<String, List<String>> classesByPXml = new HashMap<String, List<String>>();
    final List<URL> usedUrls = new ArrayList<URL>(); // for fake classloader
    for (URL url : event.getUrls()) {
      final File file = URLs.toFile(url);
      if (file.isDirectory()) {
        final String pXmls = getWarPersistenceXml(url);
        if (pXmls != null) {
          feed(classesByPXml, pXmls);
        }

        usedUrls.add(url);
      } else if (file.getName().endsWith(".jar")) {
        try {
          final JarFile jar = new JarFile(file);
          ZipEntry entry = jar.getEntry(META_INF_PERSISTENCE_XML);
          if (entry != null) {
            final String path = file.getAbsolutePath();
            final File unpacked =
                new File(path.substring(0, path.length() - 4) + TMP_ENHANCEMENT_SUFFIX);
            JarExtractor.extract(file, unpacked);

            // replace jar by folder url since otherwise enhancement doesn't work
            usedUrls.add(unpacked.toURI().toURL());

            feed(classesByPXml, new File(unpacked, META_INF_PERSISTENCE_XML).getAbsolutePath());
          }
        } catch (IOException e) {
          // ignored
        }
      } else {
        usedUrls.add(url);
      }
    }

    // enhancement

    final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
    final ClassLoader fakeClassLoader =
        new URLClassLoaderFirst(
            usedUrls.toArray(new URL[usedUrls.size()]), event.getParentClassLoader());

    Thread.currentThread().setContextClassLoader(fakeClassLoader);
    try {
      for (Map.Entry<String, List<String>> entry : classesByPXml.entrySet()) {
        final Properties opts = new Properties();
        opts.setProperty(PROPERTIES_FILE_PROP, entry.getKey());

        final Object optsArg;
        try {
          optsArg = optionsConstructor.newInstance(opts);
        } catch (Exception e) {
          LOGGER.debug("can't create options for enhancing");
          return;
        }

        LOGGER.info("enhancing url(s): " + Arrays.asList(event.getUrls()));
        try {
          enhancerMethod.invoke(null, toFilePaths(entry.getValue()), optsArg);
        } catch (Exception e) {
          LOGGER.warning("can't enhanced at deploy-time entities", e);
        }
      }
    } finally {
      Thread.currentThread().setContextClassLoader(tccl);
      usedUrls.clear();
    }

    // clean up extracted jars and replace jar to keep consistent classloading
    for (Map.Entry<String, List<String>> entry : classesByPXml.entrySet()) {
      final List<String> values = entry.getValue();
      for (String rawPath : values) {
        if (rawPath.endsWith(TMP_ENHANCEMENT_SUFFIX + "/")
            || rawPath.endsWith(TMP_ENHANCEMENT_SUFFIX)) {
          final File dir = new File(rawPath);
          final File file =
              new File(
                  rawPath.substring(0, rawPath.length() - TMP_ENHANCEMENT_SUFFIX.length() - 1)
                      + ".jar");
          if (file.exists()) {
            String name = dir.getName();
            name = name.substring(0, name.length() - TMP_ENHANCEMENT_SUFFIX.length()) + ".jar";

            final File target = new File(dir.getParentFile(), name);
            try { // override existing jar otherwise classloading is broken in tomee
              Files.delete(file);
              JarCreator.jarDir(dir, target);
            } catch (final IOException e) {
              LOGGER.error("can't repackage enhanced jar file " + file.getName());
            }
            Files.delete(dir);
          }
        }
      }
      values.clear();
    }

    classesByPXml.clear();
  }

  private void feed(final Map<String, List<String>> classesByPXml, final String pXml) {
    final List<String> paths = new ArrayList<String>();

    // first add the classes directory where is the persistence.xml
    if (pXml.endsWith(META_INF_PERSISTENCE_XML)) {
      paths.add(pXml.substring(0, pXml.length() - META_INF_PERSISTENCE_XML.length()));
    } else if (pXml.endsWith("/WEB-INF/persistence.xml")) {
      paths.add(pXml.substring(0, pXml.length() - 24));
    }

    // then jar-file
    try {
      final SAXParser parser = Saxs.factory().newSAXParser();
      final JarFileParser handler = new JarFileParser();
      parser.parse(new File(pXml), handler);
      for (String path : handler.getPaths()) {
        paths.add(relative(paths.iterator().next(), path));
      }
    } catch (Exception e) {
      LOGGER.error("can't parse '" + pXml + "'", e);
    }

    classesByPXml.put(pXml, paths);
  }

  // relativePath = relative path to the jar file containing the persistence.xml
  private String relative(final String relativePath, final String pXmlPath) {
    return new File(new File(pXmlPath).getParent(), relativePath).getAbsolutePath();
  }

  private String getWarPersistenceXml(final URL url) {
    final File dir = URLs.toFile(url);
    if (dir.isDirectory()
        && (dir.getAbsolutePath().endsWith("/WEB-INF/classes")
            || dir.getAbsolutePath().endsWith("/WEB-INF/classes/"))) {
      final File pXmlStd = new File(dir.getParentFile(), "persistence.xml");
      if (pXmlStd.exists()) {
        return pXmlStd.getAbsolutePath();
      }

      final File pXml = new File(dir, META_INF_PERSISTENCE_XML);
      if (pXml.exists()) {
        return pXml.getAbsolutePath();
      }
    }
    return null;
  }

  private String[] toFilePaths(final List<String> urls) {
    final List<String> files = new ArrayList<String>();
    for (String url : urls) {
      final File dir = new File(url);
      if (!dir.isDirectory()) {
        continue;
      }

      for (File f : Files.collect(dir, new ClassFilter())) {
        files.add(f.getAbsolutePath());
      }
    }
    return files.toArray(new String[files.size()]);
  }

  private static class ClassFilter implements FileFilter {
    private static final String DEFAULT_INCLUDE = "\\*";
    private static final String DEFAULT_EXCLUDE = "";
    private static final Pattern INCLUDE_PATTERN =
        Pattern.compile(
            SystemInstance.get()
                .getOptions()
                .get(OPENEJB_JAR_ENHANCEMENT_INCLUDE, DEFAULT_INCLUDE));
    private static final Pattern EXCLUDE_PATTERN =
        Pattern.compile(
            SystemInstance.get()
                .getOptions()
                .get(OPENEJB_JAR_ENHANCEMENT_EXCLUDE, DEFAULT_EXCLUDE));

    @Override
    public boolean accept(final File file) {
      boolean isClass = file.getName().endsWith(CLASS_EXT);
      if (DEFAULT_EXCLUDE.equals(EXCLUDE_PATTERN.pattern())
          && DEFAULT_INCLUDE.equals(INCLUDE_PATTERN.pattern())) {
        return isClass;
      }

      final String path = file.getAbsolutePath();
      return isClass
          && INCLUDE_PATTERN.matcher(path).matches()
          && !EXCLUDE_PATTERN.matcher(path).matches();
    }
  }

  private static class JarFileParser extends DefaultHandler {
    private final List<String> paths = new ArrayList<String>();
    private boolean getIt = false;

    @Override
    public void startElement(
        final String uri, final String localName, final String qName, final Attributes att)
        throws SAXException {
      if (!localName.endsWith("jar-file")) {
        return;
      }

      getIt = true;
    }

    @Override
    public void characters(final char ch[], final int start, final int length) throws SAXException {
      if (getIt) {
        paths.add(String.valueOf(ch, start, length));
      }
    }

    @Override
    public void endElement(final String uri, final String localName, final String qName)
        throws SAXException {
      getIt = false;
    }

    public List<String> getPaths() {
      return paths;
    }
  }
}
public final class ValidatorBuilder {
  public static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB_STARTUP, ValidatorBuilder.class);
  public static final String VALIDATION_PROVIDER_KEY = "openejb.bean-validation.provider";

  private ValidatorBuilder() {
    // no-op
  }

  public static ValidatorFactory buildFactory(
      final ClassLoader classLoader, final ValidationInfo info) {
    // now we will not be polluted by log build
    return buildFactory(info, classLoader);
  }

  public static ValidationInfo getInfo(final ValidationConfigType config) {
    final ValidationInfo info = new ValidationInfo();
    if (config != null) {
      info.version = config.getVersion();
      info.providerClassName = config.getDefaultProvider();
      info.constraintFactoryClass = config.getConstraintValidatorFactory();
      info.traversableResolverClass = config.getTraversableResolver();
      info.messageInterpolatorClass = config.getMessageInterpolator();
      info.parameterNameProviderClass = config.getParameterNameProvider();

      final ExecutableValidationType executableValidation = config.getExecutableValidation();
      if (executableValidation != null) {
        info.executableValidationEnabled = executableValidation.getEnabled();
        final DefaultValidatedExecutableTypesType executableTypes =
            executableValidation.getDefaultValidatedExecutableTypes();
        if (executableTypes != null) {
          for (final ExecutableType type : executableTypes.getExecutableType()) {
            info.validatedTypes.add(type.name());
          }
        }
      }
      for (final PropertyType p : config.getProperty()) {
        info.propertyTypes.put(p.getName(), p.getValue());
      }
      for (final String element : config.getConstraintMapping()) {
        info.constraintMappings.add(element);
      }
    }
    return info;
  }

  public static ValidatorFactory buildFactory(
      final ValidationInfo config, final ClassLoader classLoader) {
    ValidatorFactory factory = null;
    final Thread thread = Thread.currentThread();
    final ClassLoader oldContextLoader = thread.getContextClassLoader();
    try {
      thread.setContextClassLoader(classLoader);
      if (config == null) {
        factory = Validation.buildDefaultValidatorFactory();
      } else {
        final Configuration<?> configuration = getConfig(config);
        try {
          factory = configuration.buildValidatorFactory();
        } catch (final ValidationException ve) {
          thread.setContextClassLoader(ValidatorBuilder.class.getClassLoader());
          factory = Validation.buildDefaultValidatorFactory();
          thread.setContextClassLoader(classLoader);

          logger.warning(
              "Unable create validator factory with config "
                  + config
                  + " ("
                  + ve.getMessage()
                  + ")."
                  + " Default factory will be used.");
        }
      }
    } finally {
      thread.setContextClassLoader(oldContextLoader);
    }
    return factory;
  }

  @SuppressWarnings("unchecked")
  private static Configuration<?> getConfig(final ValidationInfo info) {
    Configuration<?> target = null;
    final Thread thread = Thread.currentThread();
    final ClassLoader classLoader = thread.getContextClassLoader();

    String providerClassName = info.providerClassName;
    if (providerClassName == null) {
      providerClassName =
          SystemInstance.get().getOptions().get(VALIDATION_PROVIDER_KEY, (String) null);
    }

    if (providerClassName != null) {
      try {
        @SuppressWarnings({"unchecked", "rawtypes"})
        final Class clazz = classLoader.loadClass(providerClassName);
        target = Validation.byProvider(clazz).configure();
        logger.info("Using " + providerClassName + " as validation provider.");
      } catch (final ClassNotFoundException e) {
        logger.warning("Unable to load provider class " + providerClassName, e);
      } catch (final ValidationException ve) {
        logger.warning(
            "Unable create validator factory with provider "
                + providerClassName
                + " ("
                + ve.getMessage()
                + ")."
                + " Default one will be used.");
      }
    }
    if (target == null) {
      // force to use container provider to ignore any conflicting configuration
      thread.setContextClassLoader(ValidatorBuilder.class.getClassLoader());
      target = Validation.byDefaultProvider().configure();
      thread.setContextClassLoader(classLoader);
    }

    final Set<ExecutableType> types = new HashSet<>();
    for (final String type : info.validatedTypes) {
      types.add(ExecutableType.valueOf(type));
    }

    final Map<String, String> props = new HashMap<>();
    for (final Map.Entry<Object, Object> entry : info.propertyTypes.entrySet()) {
      final PropertyType property = new PropertyType();
      property.setName((String) entry.getKey());
      property.setValue((String) entry.getValue());

      props.put(property.getName(), property.getValue());
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Found property '" + property.getName() + "' with value '" + property.getValue());
      }
      target.addProperty(property.getName(), property.getValue());
    }

    final OpenEjbBootstrapConfig bootstrapConfig =
        new OpenEjbBootstrapConfig(
            providerClassName,
            info.constraintFactoryClass,
            info.messageInterpolatorClass,
            info.traversableResolverClass,
            info.parameterNameProviderClass,
            new HashSet<>(info.constraintMappings),
            info.executableValidationEnabled,
            types,
            props);
    final OpenEjbConfig config = new OpenEjbConfig(bootstrapConfig, target);

    target.ignoreXmlConfiguration();

    final String messageInterpolatorClass = info.messageInterpolatorClass;
    if (messageInterpolatorClass != null) {
      try {
        @SuppressWarnings("unchecked")
        final Class<MessageInterpolator> clazz =
            (Class<MessageInterpolator>) classLoader.loadClass(messageInterpolatorClass);
        target.messageInterpolator(newInstance(config, clazz));
      } catch (final Exception e) {
        logger.warning(
            "Unable to set " + messageInterpolatorClass + " as message interpolator.", e);
      }
      logger.info("Using " + messageInterpolatorClass + " as message interpolator.");
    }
    final String traversableResolverClass = info.traversableResolverClass;
    if (traversableResolverClass != null) {
      try {
        @SuppressWarnings("unchecked")
        final Class<TraversableResolver> clazz =
            (Class<TraversableResolver>) classLoader.loadClass(traversableResolverClass);
        target.traversableResolver(newInstance(config, clazz));
      } catch (final Exception e) {
        logger.warning(
            "Unable to set " + traversableResolverClass + " as traversable resolver.", e);
      }
      logger.info("Using " + traversableResolverClass + " as traversable resolver.");
    }
    final String constraintFactoryClass = info.constraintFactoryClass;
    if (constraintFactoryClass != null) {
      try {
        @SuppressWarnings("unchecked")
        final Class<ConstraintValidatorFactory> clazz =
            (Class<ConstraintValidatorFactory>) classLoader.loadClass(constraintFactoryClass);
        target.constraintValidatorFactory(newInstance(config, clazz));
      } catch (final Exception e) {
        logger.warning("Unable to set " + constraintFactoryClass + " as constraint factory.", e);
      }
      logger.info("Using " + constraintFactoryClass + " as constraint factory.");
    }
    for (final String mappingFileName : info.constraintMappings) {
      if (logger.isDebugEnabled()) {
        logger.debug("Opening input stream for " + mappingFileName);
      }
      final InputStream in = classLoader.getResourceAsStream(mappingFileName);
      if (in == null) {
        logger.warning(
            "Unable to open input stream for mapping file "
                + mappingFileName
                + ". It will be ignored");
      } else {
        target.addMapping(in);
      }
    }
    if (info.parameterNameProviderClass != null) {
      try {
        final Class<ParameterNameProvider> clazz =
            (Class<ParameterNameProvider>) classLoader.loadClass(info.parameterNameProviderClass);
        target.parameterNameProvider(newInstance(config, clazz));
      } catch (final Exception e) {
        logger.warning(
            "Unable to set " + info.parameterNameProviderClass + " as parameter name provider.", e);
      }
      logger.info("Using " + info.parameterNameProviderClass + " as parameter name provider.");
    }

    return config;
  }

  private static <T> T newInstance(final OpenEjbConfig config, final Class<T> clazz)
      throws Exception {
    final WebBeansContext webBeansContext = WebBeansContext.currentInstance();
    if (webBeansContext == null) {
      return clazz.newInstance();
    }

    final BeanManagerImpl beanManager = webBeansContext.getBeanManagerImpl();
    if (!beanManager.isInUse()) {
      return clazz.newInstance();
    }

    final AnnotatedType<T> annotatedType = beanManager.createAnnotatedType(clazz);
    final InjectionTarget<T> it = beanManager.createInjectionTarget(annotatedType);
    final CreationalContext<T> context = beanManager.createCreationalContext(null);
    final T instance = it.produce(context);
    it.inject(instance, context);
    it.postConstruct(instance);

    config.releasables.add(new Releasable<T>(context, it, instance));

    return instance;
  }

  private static final class OpenEjbBootstrapConfig
      implements BootstrapConfiguration, Serializable {
    private final String providerClassName;
    private final String constraintFactoryClass;
    private final String messageInterpolatorClass;
    private final String traversableResolverClass;
    private final String parameterNameProviderClass;
    private final Set<String> constraintMappings;
    private final boolean executableValidationEnabled;
    private final Set<ExecutableType> validatedTypes;
    private final Map<String, String> props;

    public OpenEjbBootstrapConfig(
        final String providerClassName,
        final String constraintFactoryClass,
        final String messageInterpolatorClass,
        final String traversableResolverClass,
        final String parameterNameProviderClass,
        final Set<String> constraintMappings,
        final boolean executableValidationEnabled,
        final Set<ExecutableType> validatedTypes,
        final Map<String, String> props) {
      this.providerClassName = providerClassName;
      this.constraintFactoryClass = constraintFactoryClass;
      this.messageInterpolatorClass = messageInterpolatorClass;
      this.traversableResolverClass = traversableResolverClass;
      this.parameterNameProviderClass = parameterNameProviderClass;
      this.constraintMappings = constraintMappings;
      this.executableValidationEnabled = executableValidationEnabled;
      this.validatedTypes = validatedTypes;
      this.props = props;
    }

    @Override
    public String getDefaultProviderClassName() {
      return providerClassName;
    }

    @Override
    public String getConstraintValidatorFactoryClassName() {
      return constraintFactoryClass;
    }

    @Override
    public String getMessageInterpolatorClassName() {
      return messageInterpolatorClass;
    }

    @Override
    public String getTraversableResolverClassName() {
      return traversableResolverClass;
    }

    @Override
    public String getParameterNameProviderClassName() {
      return parameterNameProviderClass;
    }

    @Override
    public Set<String> getConstraintMappingResourcePaths() {
      return constraintMappings;
    }

    @Override
    public boolean isExecutableValidationEnabled() {
      return executableValidationEnabled;
    }

    @Override
    public Set<ExecutableType> getDefaultValidatedExecutableTypes() {
      return validatedTypes;
    }

    @Override
    public Map<String, String> getProperties() {
      return props;
    }
  }

  private static final class OpenEjbConfig<T extends Configuration<T>> implements Configuration<T> {
    private final Collection<Releasable<?>> releasables = new LinkedList<>();
    private final Configuration<T> delegate;
    private final BootstrapConfiguration bootstrap;

    public OpenEjbConfig(
        final BootstrapConfiguration bootstrapConfig, final Configuration<T> target) {
      bootstrap = bootstrapConfig;
      delegate = target;
    }

    @Override
    public T ignoreXmlConfiguration() {
      return delegate.ignoreXmlConfiguration();
    }

    @Override
    public T messageInterpolator(final MessageInterpolator interpolator) {
      return delegate.messageInterpolator(interpolator);
    }

    @Override
    public T traversableResolver(final TraversableResolver resolver) {
      return delegate.traversableResolver(resolver);
    }

    @Override
    public T constraintValidatorFactory(
        final ConstraintValidatorFactory constraintValidatorFactory) {
      return delegate.constraintValidatorFactory(constraintValidatorFactory);
    }

    @Override
    public T addMapping(final InputStream stream) {
      return delegate.addMapping(stream);
    }

    @Override
    public T addProperty(final String name, final String value) {
      return delegate.addProperty(name, value);
    }

    @Override
    public MessageInterpolator getDefaultMessageInterpolator() {
      return delegate.getDefaultMessageInterpolator();
    }

    @Override
    public TraversableResolver getDefaultTraversableResolver() {
      return delegate.getDefaultTraversableResolver();
    }

    @Override
    public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
      return delegate.getDefaultConstraintValidatorFactory();
    }

    @Override
    public ValidatorFactory buildValidatorFactory() {
      return new OpenEJBValidatorFactory(delegate.buildValidatorFactory(), releasables);
    }

    @Override
    public T parameterNameProvider(ParameterNameProvider parameterNameProvider) {
      return delegate.parameterNameProvider(parameterNameProvider);
    }

    @Override
    public ParameterNameProvider getDefaultParameterNameProvider() {
      return delegate.getDefaultParameterNameProvider();
    }

    @Override
    public BootstrapConfiguration getBootstrapConfiguration() {
      return bootstrap;
    }
  }

  private static final class OpenEJBValidatorFactory implements ValidatorFactory {
    private final ValidatorFactory delegate;
    private final Collection<Releasable<?>> toRelease;

    public OpenEJBValidatorFactory(
        final ValidatorFactory validatorFactory, final Collection<Releasable<?>> releasables) {
      delegate = validatorFactory;
      toRelease = releasables;
    }

    @Override
    public Validator getValidator() {
      return delegate.getValidator();
    }

    @Override
    public ValidatorContext usingContext() {
      return delegate.usingContext();
    }

    @Override
    public MessageInterpolator getMessageInterpolator() {
      return delegate.getMessageInterpolator();
    }

    @Override
    public TraversableResolver getTraversableResolver() {
      return delegate.getTraversableResolver();
    }

    @Override
    public ConstraintValidatorFactory getConstraintValidatorFactory() {
      return delegate.getConstraintValidatorFactory();
    }

    @Override
    public <T> T unwrap(final Class<T> type) {
      return delegate.unwrap(type);
    }

    @Override
    public ParameterNameProvider getParameterNameProvider() {
      return delegate.getParameterNameProvider();
    }

    @Override
    public void close() {
      delegate.close();
      for (final Releasable<?> r : toRelease) {
        r.release();
      }
    }
  }

  private static final class Releasable<T> {
    private final CreationalContext<T> context;
    private final InjectionTarget<T> injectionTarget;
    private final T instance;

    private Releasable(
        final CreationalContext<T> context,
        final InjectionTarget<T> injectionTarget,
        final T instance) {
      this.context = context;
      this.injectionTarget = injectionTarget;
      this.instance = instance;
    }

    private void release() {
      try {
        injectionTarget.preDestroy(instance);
        injectionTarget.dispose(instance);
        context.release();
      } catch (final Exception | NoClassDefFoundError e) {
        // no-op
      }
    }
  }
}
Beispiel #5
0
public class InitEjbDeployments implements DynamicDeployer {
  public static Messages messages = new Messages("org.apache.openejb.util.resources");
  public static Logger logger =
      Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");

  private final StringTemplate deploymentIdTemplate;
  private static final String DEPLOYMENT_ID_FORMAT = "openejb.deploymentId.format";

  public InitEjbDeployments() {
    final String format = SystemInstance.get().getOptions().get(DEPLOYMENT_ID_FORMAT, "{ejbName}");
    this.deploymentIdTemplate = new StringTemplate(format);
  }

  public synchronized AppModule deploy(final AppModule appModule) throws OpenEJBException {

    final Set<String> abstractSchemaNames = new HashSet<String>();
    for (final EjbModule ejbModule : appModule.getEjbModules()) {
      for (final EnterpriseBean bean : ejbModule.getEjbJar().getEnterpriseBeans()) {
        if (isCmpEntity(bean)) {
          final EntityBean entity = (EntityBean) bean;
          final String name = entity.getAbstractSchemaName();
          if (name != null) {
            abstractSchemaNames.add(name);
          }
        }
      }
    }

    final Map<String, String> contextData = new HashMap<String, String>();
    contextData.put("appId", appModule.getModuleId());

    for (final EjbModule ejbModule : appModule.getEjbModules()) {
      contextData.put("ejbJarId", ejbModule.getModuleId());
      deploy(ejbModule, contextData, abstractSchemaNames);
    }
    contextData.clear();
    return appModule;
  }

  public EjbModule deploy(final EjbModule ejbModule) throws OpenEJBException {
    return deploy(ejbModule, new HashMap<String, String>(), new HashSet<String>());
  }

  private EjbModule deploy(
      final EjbModule ejbModule,
      final Map<String, String> contextData,
      final Set<String> abstractSchemaNames)
      throws OpenEJBException {
    contextData.put("moduleId", ejbModule.getModuleId());
    contextData.put("moduleUri", ejbModule.getModuleUri().toString());

    final OpenejbJar openejbJar;
    if (ejbModule.getOpenejbJar() != null) {
      openejbJar = ejbModule.getOpenejbJar();
    } else {
      openejbJar = new OpenejbJar();
      ejbModule.setOpenejbJar(openejbJar);
    }

    StringTemplate deploymentIdTemplate = this.deploymentIdTemplate;
    if (openejbJar.getProperties().containsKey(DEPLOYMENT_ID_FORMAT)) {
      final String format = openejbJar.getProperties().getProperty(DEPLOYMENT_ID_FORMAT);
      logger.info("Using " + DEPLOYMENT_ID_FORMAT + " '" + format + "'");
      deploymentIdTemplate = new StringTemplate(format);
    }

    for (final EnterpriseBean bean : ejbModule.getEjbJar().getEnterpriseBeans()) {
      StringTemplate template = deploymentIdTemplate;

      final org.apache.openejb.api.EjbDeployment annotation =
          getEjbDeploymentAnnotation(ejbModule, bean);

      EjbDeployment ejbDeployment = openejbJar.getDeploymentsByEjbName().get(bean.getEjbName());
      if (ejbDeployment == null) {

        ejbDeployment = new EjbDeployment();

        ejbDeployment.setEjbName(bean.getEjbName());

        if (annotation != null && isDefined(annotation.id())) {
          template = new StringTemplate(annotation.id());
          ejbDeployment.setDeploymentId(formatDeploymentId(bean, contextData, template));
        } else {
          ejbDeployment.setDeploymentId(formatDeploymentId(bean, contextData, template));
          if (!(bean instanceof ManagedBean) || !((ManagedBean) bean).isHidden()) {
            logger.info(
                "Auto-deploying ejb "
                    + bean.getEjbName()
                    + ": EjbDeployment(deployment-id="
                    + ejbDeployment.getDeploymentId()
                    + ")");
          }
        }

        openejbJar.getEjbDeployment().add(ejbDeployment);
      } else if (ejbDeployment.getDeploymentId() == null) {
        if (annotation != null && isDefined(annotation.id())) {
          template = new StringTemplate(annotation.id());
          ejbDeployment.setDeploymentId(formatDeploymentId(bean, contextData, template));
        } else {
          ejbDeployment.setDeploymentId(formatDeploymentId(bean, contextData, template));
          logger.info(
              "Auto-assigning deployment-id for ejb "
                  + bean.getEjbName()
                  + ": EjbDeployment(deployment-id="
                  + ejbDeployment.getDeploymentId()
                  + ")");
        }
      }

      if (ejbDeployment.getContainerId() == null
          && annotation != null
          && isDefined(annotation.container())) {
        ejbDeployment.setContainerId(annotation.container());
      }

      if (isCmpEntity(bean)) {
        final EntityBean entity = (EntityBean) bean;
        if (entity.getAbstractSchemaName() == null) {
          String abstractSchemaName = bean.getEjbName().trim().replaceAll("[ \\t\\n\\r-]+", "_");

          // The AbstractSchemaName must be unique, we should check that it is
          if (abstractSchemaNames.contains(abstractSchemaName)) {
            int i = 2;
            while (abstractSchemaNames.contains(abstractSchemaName + i)) {
              i++;
            }
            abstractSchemaName = abstractSchemaName + i;
          }

          abstractSchemaNames.add(abstractSchemaName);
          entity.setAbstractSchemaName(abstractSchemaName);
        }
      }
    }

    return ejbModule;
  }

  private org.apache.openejb.api.EjbDeployment getEjbDeploymentAnnotation(
      final EjbModule ejbModule, final EnterpriseBean bean) {
    try {
      final Class<?> beanClass = ejbModule.getClassLoader().loadClass(bean.getEjbClass());
      return beanClass.getAnnotation(org.apache.openejb.api.EjbDeployment.class);
    } catch (final ClassNotFoundException e) {
      // this should never happen, the class has already been loaded a ton of times by this point
      // unfortunately we have some unit tests that prevent us from throwing an exception just in
      // case
      // Those are OpenEjb2ConversionTest and SunCmpConversionTest
      return null;
    }
  }

  private boolean isDefined(final String s) {
    return s != null && !"".equals(s);
  }

  private static boolean isCmpEntity(final EnterpriseBean bean) {
    return bean instanceof EntityBean
        && ((EntityBean) bean).getPersistenceType() == PersistenceType.CONTAINER;
  }

  private String formatDeploymentId(
      final EnterpriseBean bean,
      final Map<String, String> contextData,
      final StringTemplate template) {
    contextData.put("ejbType", bean.getClass().getSimpleName());
    contextData.put("ejbClass", bean.getEjbClass());

    // we don't have the ejb class object (only the string name) so we have
    // to extract the simple name from the FQN of the class
    final int simpleNameIdx = bean.getEjbClass().lastIndexOf(".");
    contextData.put("ejbClass.simpleName", bean.getEjbClass().substring(simpleNameIdx + 1));

    contextData.put("ejbName", bean.getEjbName());

    final String name = template.apply(contextData);
    if (bean
        instanceof
        CompManagedBean) { // avoid conflict in ear between an ejbmodule and a war using the same
                           // name
      return name + System.identityHashCode(bean);
    }
    return name;
  }
}
public class ManagedContainer implements RpcContainer {
  private static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");

  private final Object containerID;
  private final SecurityService securityService;

  // todo this should be part of the constructor
  protected final JtaEntityManagerRegistry entityManagerRegistry =
      SystemInstance.get().getComponent(JtaEntityManagerRegistry.class);

  /** Index used for getDeployments() and getDeploymentInfo(deploymentId). */
  protected final Map<Object, BeanContext> deploymentsById = new HashMap<Object, BeanContext>();

  protected final Cache<Object, Instance> cache;
  private final ConcurrentHashMap<Object, Instance> checkedOutInstances =
      new ConcurrentHashMap<Object, Instance>();
  private final SessionContext sessionContext;

  public ManagedContainer(Object id, SecurityService securityService) throws SystemException {
    this.cache =
        new SimpleCache<Object, Instance>(
            null, new SimplePassivater(), 1000, 50, new Duration("1 hour"));
    this.containerID = id;
    this.securityService = securityService;
    cache.setListener(new StatefulCacheListener());
    sessionContext =
        new ManagedContext(
            securityService,
            new ManagedUserTransaction(new EjbUserTransaction(), entityManagerRegistry));
  }

  private Map<Method, MethodType> getLifecycleMethodsOfInterface(BeanContext beanContext) {
    Map<Method, MethodType> methods = new HashMap<Method, MethodType>();

    List<Method> removeMethods = beanContext.getRemoveMethods();
    for (Method removeMethod : removeMethods) {
      methods.put(removeMethod, MethodType.REMOVE);

      for (Class businessLocal : beanContext.getBusinessLocalInterfaces()) {
        try {
          Method method = businessLocal.getMethod(removeMethod.getName());
          methods.put(method, MethodType.REMOVE);
        } catch (NoSuchMethodException thatsFine) {
        }
      }

      for (Class businessRemote : beanContext.getBusinessRemoteInterfaces()) {
        try {
          Method method = businessRemote.getMethod(removeMethod.getName());
          methods.put(method, MethodType.REMOVE);
        } catch (NoSuchMethodException thatsFine) {
        }
      }
    }

    Class legacyRemote = beanContext.getRemoteInterface();
    if (legacyRemote != null) {
      try {
        Method method = legacyRemote.getMethod("remove");
        methods.put(method, MethodType.REMOVE);
      } catch (NoSuchMethodException thatsFine) {
      }
    }

    Class legacyLocal = beanContext.getLocalInterface();
    if (legacyLocal != null) {
      try {
        Method method = legacyLocal.getMethod("remove");
        methods.put(method, MethodType.REMOVE);
      } catch (NoSuchMethodException thatsFine) {
      }
    }

    Class businessLocalHomeInterface = beanContext.getBusinessLocalInterface();
    if (businessLocalHomeInterface != null) {
      for (Method method : BeanContext.BusinessLocalHome.class.getMethods()) {
        if (method.getName().startsWith("create")) {
          methods.put(method, MethodType.CREATE);
        } else if (method.getName().equals("remove")) {
          methods.put(method, MethodType.REMOVE);
        }
      }
    }

    Class businessLocalBeanHomeInterface = beanContext.getBusinessLocalBeanInterface();
    if (businessLocalBeanHomeInterface != null) {
      for (Method method : BeanContext.BusinessLocalBeanHome.class.getMethods()) {
        if (method.getName().startsWith("create")) {
          methods.put(method, MethodType.CREATE);
        } else if (method.getName().equals("remove")) {
          methods.put(method, MethodType.REMOVE);
        }
      }
    }

    Class businessRemoteHomeInterface = beanContext.getBusinessRemoteInterface();
    if (businessRemoteHomeInterface != null) {
      for (Method method : BeanContext.BusinessRemoteHome.class.getMethods()) {
        if (method.getName().startsWith("create")) {
          methods.put(method, MethodType.CREATE);
        } else if (method.getName().equals("remove")) {
          methods.put(method, MethodType.REMOVE);
        }
      }
    }

    Class homeInterface = beanContext.getHomeInterface();
    if (homeInterface != null) {
      for (Method method : homeInterface.getMethods()) {
        if (method.getName().startsWith("create")) {
          methods.put(method, MethodType.CREATE);
        } else if (method.getName().equals("remove")) {
          methods.put(method, MethodType.REMOVE);
        }
      }
    }

    Class localHomeInterface = beanContext.getLocalHomeInterface();
    if (localHomeInterface != null) {
      for (Method method : localHomeInterface.getMethods()) {
        if (method.getName().startsWith("create")) {
          methods.put(method, MethodType.CREATE);
        } else if (method.getName().equals("remove")) {
          methods.put(method, MethodType.REMOVE);
        }
      }
    }
    return methods;
  }

  public static enum MethodType {
    CREATE,
    REMOVE,
    BUSINESS
  }

  public ContainerType getContainerType() {
    return ContainerType.STATEFUL;
  }

  public Object getContainerID() {
    return containerID;
  }

  public synchronized BeanContext[] getBeanContexts() {
    return deploymentsById.values().toArray(new BeanContext[deploymentsById.size()]);
  }

  public synchronized BeanContext getBeanContext(Object deploymentID) {
    return deploymentsById.get(deploymentID);
  }

  public void start(BeanContext beanContext) throws OpenEJBException {}

  public void stop(BeanContext beanContext) throws OpenEJBException {}

  public synchronized void undeploy(final BeanContext bean) throws OpenEJBException {
    Data data = (Data) bean.getContainerData();

    MBeanServer server = LocalMBeanServer.get();
    for (ObjectName objectName : data.jmxNames) {
      try {
        server.unregisterMBean(objectName);
      } catch (Exception e) {
        logger.error("Unable to unregister MBean " + objectName);
      }
    }

    deploymentsById.remove(bean.getDeploymentID());
    bean.setContainer(null);
    bean.setContainerData(null);

    cache.removeAll(
        new CacheFilter<Instance>() {
          public boolean matches(Instance instance) {
            return bean == instance.beanContext;
          }
        });
  }

  public synchronized void deploy(BeanContext beanContext) throws OpenEJBException {
    Map<Method, MethodType> methods = getLifecycleMethodsOfInterface(beanContext);

    deploymentsById.put(beanContext.getDeploymentID(), beanContext);
    beanContext.setContainer(this);
    Data data = new Data(new Index<Method, MethodType>(methods));
    beanContext.setContainerData(data);

    // Create stats interceptor
    StatsInterceptor stats = new StatsInterceptor(beanContext.getBeanClass());
    beanContext.addSystemInterceptor(stats);

    MBeanServer server = LocalMBeanServer.get();

    ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb.management");
    jmxName.set("J2EEServer", "openejb");
    jmxName.set("J2EEApplication", null);
    jmxName.set("EJBModule", beanContext.getModuleID());
    jmxName.set("StatelessSessionBean", beanContext.getEjbName());
    jmxName.set("j2eeType", "");
    jmxName.set("name", beanContext.getEjbName());

    // register the invocation stats interceptor
    try {
      ObjectName objectName = jmxName.set("j2eeType", "Invocations").build();
      server.registerMBean(new ManagedMBean(stats), objectName);
      data.jmxNames.add(objectName);
    } catch (Exception e) {
      logger.error("Unable to register MBean ", e);
    }

    try {
      final Context context = beanContext.getJndiEnc();
      context.bind("comp/EJBContext", sessionContext);
    } catch (NamingException e) {
      throw new OpenEJBException("Failed to bind EJBContext", e);
    }

    beanContext.set(EJBContext.class, this.sessionContext);
  }

  /** @deprecated use invoke signature without 'securityIdentity' argument. */
  public Object invoke(
      Object deployID, Method callMethod, Object[] args, Object primKey, Object securityIdentity)
      throws OpenEJBException {
    return invoke(deployID, null, callMethod.getDeclaringClass(), callMethod, args, primKey);
  }

  public Object invoke(
      Object deployID, Class callInterface, Method callMethod, Object[] args, Object primKey)
      throws OpenEJBException {
    return invoke(deployID, null, callInterface, callMethod, args, primKey);
  }

  public Object invoke(
      Object deployID,
      InterfaceType type,
      Class callInterface,
      Method callMethod,
      Object[] args,
      Object primKey)
      throws OpenEJBException {
    BeanContext beanContext = this.getBeanContext(deployID);

    if (beanContext == null)
      throw new OpenEJBException(
          "Deployment does not exist in this container. Deployment(id='"
              + deployID
              + "'), Container(id='"
              + containerID
              + "')");

    // Use the backup way to determine call type if null was supplied.
    if (type == null) type = beanContext.getInterfaceType(callInterface);

    Data data = (Data) beanContext.getContainerData();
    MethodType methodType = data.getMethodIndex().get(callMethod);
    methodType = (methodType != null) ? methodType : MethodType.BUSINESS;

    switch (methodType) {
      case CREATE:
        return createEJBObject(beanContext, callMethod, args, type);
      case REMOVE:
        return removeEJBObject(beanContext, primKey, callInterface, callMethod, args, type);
      default:
        return businessMethod(beanContext, primKey, callInterface, callMethod, args, type);
    }
  }

  protected ProxyInfo createEJBObject(
      BeanContext beanContext, Method callMethod, Object[] args, InterfaceType interfaceType)
      throws OpenEJBException {
    // generate a new primary key
    Object primaryKey = newPrimaryKey();

    ThreadContext createContext = new ThreadContext(beanContext, primaryKey);
    ThreadContext oldCallContext = ThreadContext.enter(createContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // Create the extended entity managers for this instance
      Index<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker> entityManagers =
          createEntityManagers(beanContext);

      // Register the newly created entity managers
      if (entityManagers != null) {
        try {
          entityManagerRegistry.addEntityManagers(
              (String) beanContext.getDeploymentID(), primaryKey, entityManagers);
        } catch (EntityManagerAlreadyRegisteredException e) {
          throw new EJBException(e);
        }
      }

      createContext.setCurrentOperation(Operation.CREATE);
      createContext.setCurrentAllowedStates(null);

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              createContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              createContext);

      Instance instance = null;
      try {
        // Create new instance

        try {
          final InstanceContext context = beanContext.newInstance();

          // Wrap-up everthing into a object
          instance =
              new Instance(
                  beanContext,
                  primaryKey,
                  context.getBean(),
                  context.getInterceptors(),
                  context.getCreationalContext(),
                  entityManagers);

        } catch (Throwable throwable) {
          ThreadContext callContext = ThreadContext.getThreadContext();
          handleSystemException(callContext.getTransactionPolicy(), throwable, callContext);
          throw new IllegalStateException(throwable); // should never be reached
        }

        // add to cache
        cache.add(primaryKey, instance);

        // instance starts checked-out
        checkedOutInstances.put(primaryKey, instance);

        // Register for synchronization callbacks
        registerSessionSynchronization(instance, createContext);

        // Invoke create for legacy beans
        if (!callMethod.getDeclaringClass().equals(BeanContext.BusinessLocalHome.class)
            && !callMethod.getDeclaringClass().equals(BeanContext.BusinessRemoteHome.class)
            && !callMethod.getDeclaringClass().equals(BeanContext.BusinessLocalBeanHome.class)) {

          // Setup for business invocation
          Method createOrInit = beanContext.getMatchingBeanMethod(callMethod);
          createContext.set(Method.class, createOrInit);

          // Initialize interceptor stack
          InterceptorStack interceptorStack =
              new InterceptorStack(
                  instance.bean,
                  createOrInit,
                  Operation.CREATE,
                  new ArrayList<InterceptorData>(),
                  new HashMap<String, Object>());

          // Invoke
          if (args == null) {
            interceptorStack.invoke();
          } else {
            interceptorStack.invoke(args);
          }
        }
      } catch (Throwable e) {
        handleException(createContext, txPolicy, e);
      } finally {
        afterInvoke(createContext, txPolicy, instance);
      }

      return new ProxyInfo(beanContext, primaryKey);
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }

  protected Object newPrimaryKey() {
    return new VMID();
  }

  protected Object removeEJBObject(
      BeanContext beanContext,
      Object primKey,
      Class callInterface,
      Method callMethod,
      Object[] args,
      InterfaceType interfaceType)
      throws OpenEJBException {
    if (primKey == null) throw new NullPointerException("primKey is null");

    ThreadContext callContext = new ThreadContext(beanContext, primKey);
    ThreadContext oldCallContext = ThreadContext.enter(callContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // If a bean managed transaction is active, the bean can not be removed
      if (interfaceType.isComponent()) {
        Instance instance = checkedOutInstances.get(primKey);

        /**
         * According to EJB 3.0 "4.4.4 Restrictions for Transactions" any remove methods from home
         * or component interfaces must not be allowed if the bean instance is in a transaction.
         * Unfortunately, the Java EE 5 TCK has tests that ignore the restrictions in 4.4.4 and
         * expect beans in transactions can be removed via their home or component interface. The
         * test to see if the bean instance implements javax.ejb.SessionBean is a workaround for
         * passing the TCK while the tests in question can be challenged or the spec can be
         * changed/updated.
         */
        if (instance != null && instance.bean instanceof javax.ejb.SessionBean) {
          throw new ApplicationException(
              new RemoveException("A stateful EJB enrolled in a transaction can not be removed"));
        }
      }

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              callContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              callContext);

      Object returnValue = null;
      boolean retain = false;
      Instance instance = null;
      Method runMethod = null;
      try {
        // Obtain instance
        instance = obtainInstance(primKey, callContext);

        // Resume previous Bean transaction if there was one
        if (txPolicy instanceof BeanTransactionPolicy) {
          // Resume previous Bean transaction if there was one
          SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
          if (suspendedTransaction != null) {
            instance.setBeanTransaction(null);
            BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
            beanTxEnv.resumeUserTransaction(suspendedTransaction);
          }
        }

        // Register the entity managers
        registerEntityManagers(instance, callContext);

        // Register for synchronization callbacks
        registerSessionSynchronization(instance, callContext);

        // Setup for remove invocation
        callContext.setCurrentOperation(Operation.REMOVE);
        callContext.setCurrentAllowedStates(null);
        callContext.setInvokedInterface(callInterface);
        runMethod = beanContext.getMatchingBeanMethod(callMethod);
        callContext.set(Method.class, runMethod);

        // Do not pass arguments on home.remove(remote) calls
        Class<?> declaringClass = callMethod.getDeclaringClass();
        if (declaringClass.equals(EJBHome.class) || declaringClass.equals(EJBLocalHome.class)) {
          args = new Object[] {};
        }

        // Initialize interceptor stack
        List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, runMethod, Operation.REMOVE, interceptors, instance.interceptors);

        // Invoke
        if (args == null) {
          returnValue = interceptorStack.invoke();
        } else {
          returnValue = interceptorStack.invoke(args);
        }
      } catch (InvalidateReferenceException e) {
        throw e;
      } catch (Throwable e) {
        if (interfaceType.isBusiness()) {
          retain = beanContext.retainIfExeption(runMethod);
          handleException(callContext, txPolicy, e);
        } else {
          try {
            handleException(callContext, txPolicy, e);
          } catch (ApplicationException ae) {
            // Don't throw application exceptions for non-business interface removes
          }
        }
      } finally {
        if (!retain) {
          try {
            callContext.setCurrentOperation(Operation.PRE_DESTROY);
            List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
            InterceptorStack interceptorStack =
                new InterceptorStack(
                    instance.bean,
                    null,
                    Operation.PRE_DESTROY,
                    callbackInterceptors,
                    instance.interceptors);
            interceptorStack.invoke();
          } catch (Throwable callbackException) {
            String logMessage =
                "An unexpected exception occured while invoking the preDestroy method on the removed Stateful SessionBean instance; "
                    + callbackException.getClass().getName()
                    + " "
                    + callbackException.getMessage();

            /* [1] Log the exception or error */
            logger.error(logMessage);

          } finally {
            callContext.setCurrentOperation(Operation.REMOVE);
          }

          // todo destroy extended persistence contexts
          discardInstance(callContext);
        }

        // Commit transaction
        afterInvoke(callContext, txPolicy, instance);
      }

      return returnValue;
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }

  protected Object businessMethod(
      BeanContext beanContext,
      Object primKey,
      Class callInterface,
      Method callMethod,
      Object[] args,
      InterfaceType interfaceType)
      throws OpenEJBException {
    ThreadContext callContext = new ThreadContext(beanContext, primKey);
    ThreadContext oldCallContext = ThreadContext.enter(callContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              callContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              callContext);

      Object returnValue = null;
      Instance instance = null;
      try {
        // Obtain instance
        instance = obtainInstance(primKey, callContext);

        // Resume previous Bean transaction if there was one
        if (txPolicy instanceof BeanTransactionPolicy) {
          SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
          if (suspendedTransaction != null) {
            instance.setBeanTransaction(null);
            BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
            beanTxEnv.resumeUserTransaction(suspendedTransaction);
          }
        }

        // Register the entity managers
        registerEntityManagers(instance, callContext);
        // Register for synchronization callbacks
        registerSessionSynchronization(instance, callContext);

        // Setup for business invocation
        callContext.setCurrentOperation(Operation.BUSINESS);
        callContext.setCurrentAllowedStates(null);
        callContext.setInvokedInterface(callInterface);
        Method runMethod = beanContext.getMatchingBeanMethod(callMethod);
        callContext.set(Method.class, runMethod);

        // Initialize interceptor stack
        List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, runMethod, Operation.BUSINESS, interceptors, instance.interceptors);

        // Invoke
        returnValue = interceptorStack.invoke(args);
      } catch (Throwable e) {
        handleException(callContext, txPolicy, e);
      } finally {
        // Commit transaction
        afterInvoke(callContext, txPolicy, instance);
      }
      return returnValue;
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }

  private Instance obtainInstance(Object primaryKey, ThreadContext callContext)
      throws OpenEJBException {
    if (primaryKey == null) {
      throw new SystemException(
          new NullPointerException(
              "Cannot obtain an instance of the stateful session bean with a null session id"));
    }

    Transaction currentTransaction = getTransaction(callContext);

    // Find the instance
    Instance instance = checkedOutInstances.get(primaryKey);
    if (instance == null) {
      try {
        instance = cache.checkOut(primaryKey);
      } catch (OpenEJBException e) {
        throw e;
      } catch (Exception e) {
        throw new SystemException("Unexpected load exception", e);
      }

      // Did we find the instance?
      if (instance == null) {
        throw new InvalidateReferenceException(new NoSuchObjectException("Not Found"));
      }

      // remember instance until it is returned to the cache
      checkedOutInstances.put(primaryKey, instance);
    }

    synchronized (instance) {
      // Is the instance alreayd in use?
      if (instance.isInUse()) {
        // the bean is already being invoked; the only reentrant/concurrent operations allowed are
        // Session synchronization callbacks
        Operation currentOperation = callContext.getCurrentOperation();
        if (currentOperation != Operation.AFTER_COMPLETION
            && currentOperation != Operation.BEFORE_COMPLETION) {
          throw new ApplicationException(new RemoteException("Concurrent calls not allowed."));
        }
      }

      if (instance.getTransaction() != null) {
        if (!instance.getTransaction().equals(currentTransaction)
            && !instance.getLock().tryLock()) {
          throw new ApplicationException(
              new RemoteException(
                  "Instance is in a transaction and cannot be invoked outside that transaction.  See EJB 3.0 Section 4.4.4"));
        }
      } else {
        instance.setTransaction(currentTransaction);
      }

      // Mark the instance in use so we can detect reentrant calls
      instance.setInUse(true);

      return instance;
    }
  }

  private Transaction getTransaction(ThreadContext callContext) {
    TransactionPolicy policy = callContext.getTransactionPolicy();

    Transaction currentTransaction = null;
    if (policy instanceof JtaTransactionPolicy) {
      JtaTransactionPolicy jtaPolicy = (JtaTransactionPolicy) policy;

      currentTransaction = jtaPolicy.getCurrentTransaction();
    }
    return currentTransaction;
  }

  private void releaseInstance(Instance instance) {
    // Don't pool if the bean has been undeployed
    if (instance.beanContext.isDestroyed()) return;

    // verify the instance is not associated with a bean-managed transaction
    if (instance.getBeanTransaction() != null) {
      new IllegalStateException("Instance has an active bean-managed transaction");
    }

    // no longer in use
    instance.setInUse(false);

    if (instance.getTransaction() == null) {
      // return to cache
      cache.checkIn(instance.primaryKey);

      // no longer checked out
      checkedOutInstances.remove(instance.primaryKey);
    }
  }

  private void discardInstance(ThreadContext threadContext) {
    Object primaryKey = threadContext.getPrimaryKey();
    if (primaryKey == null) {
      return;
    }

    Instance instance = checkedOutInstances.remove(primaryKey);
    cache.remove(primaryKey);
    if (instance.creationalContext != null) {
      instance.creationalContext.release();
    }
  }

  private void checkAuthorization(Method callMethod, InterfaceType interfaceType)
      throws ApplicationException {
    boolean authorized = securityService.isCallerAuthorized(callMethod, interfaceType);
    if (!authorized) {
      throw new ApplicationException(
          new EJBAccessException("Unauthorized Access by Principal Denied"));
    }
  }

  private void handleException(ThreadContext callContext, TransactionPolicy txPolicy, Throwable e)
      throws ApplicationException {
    if (e instanceof ApplicationException) {
      throw (ApplicationException) e;
    }

    ExceptionType type = callContext.getBeanContext().getExceptionType(e);
    if (type == SYSTEM) {
      discardInstance(callContext);
      handleSystemException(txPolicy, e, callContext);
    } else {
      handleApplicationException(txPolicy, e, type == APPLICATION_ROLLBACK);
    }
  }

  private void afterInvoke(ThreadContext callContext, TransactionPolicy txPolicy, Instance instance)
      throws OpenEJBException {
    try {
      unregisterEntityManagers(instance, callContext);
      if (instance != null && txPolicy instanceof BeanTransactionPolicy) {
        // suspend the currently running transaction if any
        SuspendedTransaction suspendedTransaction = null;
        try {
          BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
          suspendedTransaction = beanTxEnv.suspendUserTransaction();
        } catch (SystemException e) {
          handleSystemException(txPolicy, e, callContext);
        } finally {
          instance.setBeanTransaction(suspendedTransaction);
        }
      }
    } finally {
      if (instance != null) {
        instance.setInUse(false);
      }
      EjbTransactionUtil.afterInvoke(txPolicy, callContext);
    }
  }

  private Index<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker>
      createEntityManagers(BeanContext beanContext) {
    // create the extended entity managers
    Index<EntityManagerFactory, Map> factories = beanContext.getExtendedEntityManagerFactories();
    Index<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker> entityManagers =
        null;
    if (factories != null && factories.size() > 0) {
      entityManagers =
          new Index<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker>(
              new ArrayList<EntityManagerFactory>(factories.keySet()));
      for (Map.Entry<EntityManagerFactory, Map> entry : factories.entrySet()) {
        EntityManagerFactory entityManagerFactory = entry.getKey();
        Map properties = entry.getValue();

        JtaEntityManagerRegistry.EntityManagerTracker entityManagerTracker =
            entityManagerRegistry.getInheritedEntityManager(entityManagerFactory);
        EntityManager entityManager;
        if (entityManagerTracker == null) {
          if (properties != null) {
            entityManager = entityManagerFactory.createEntityManager(properties);
          } else {
            entityManager = entityManagerFactory.createEntityManager();
          }
          entityManagerTracker = new JtaEntityManagerRegistry.EntityManagerTracker(entityManager);
        } else {
          entityManagerTracker.incCounter();
        }
        entityManagers.put(entityManagerFactory, entityManagerTracker);
      }
    }
    return entityManagers;
  }

  private void registerEntityManagers(Instance instance, ThreadContext callContext)
      throws OpenEJBException {
    if (entityManagerRegistry == null) return;

    BeanContext beanContext = callContext.getBeanContext();

    // get the factories
    Index<EntityManagerFactory, Map> factories = beanContext.getExtendedEntityManagerFactories();
    if (factories == null) return;

    // get the managers for the factories
    Map<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker> entityManagers =
        instance.getEntityManagers(factories);
    if (entityManagers == null) return;

    // register them
    try {
      entityManagerRegistry.addEntityManagers(
          (String) beanContext.getDeploymentID(), instance.primaryKey, entityManagers);
    } catch (EntityManagerAlreadyRegisteredException e) {
      throw new EJBException(e);
    }
  }

  private void unregisterEntityManagers(Instance instance, ThreadContext callContext) {
    if (entityManagerRegistry == null) return;
    if (instance == null) return;

    BeanContext beanContext = callContext.getBeanContext();

    // register them
    entityManagerRegistry.removeEntityManagers(
        (String) beanContext.getDeploymentID(), instance.primaryKey);
  }

  private void registerSessionSynchronization(Instance instance, ThreadContext callContext) {
    TransactionPolicy txPolicy = callContext.getTransactionPolicy();
    if (txPolicy == null) {
      throw new IllegalStateException("ThreadContext does not contain a TransactionEnvironment");
    }

    SessionSynchronizationCoordinator coordinator =
        (SessionSynchronizationCoordinator)
            txPolicy.getResource(SessionSynchronizationCoordinator.class);
    if (coordinator == null) {
      coordinator = new SessionSynchronizationCoordinator(txPolicy);
      txPolicy.registerSynchronization(coordinator);
      txPolicy.putResource(SessionSynchronizationCoordinator.class, coordinator);
    }

    // SessionSynchronization are only enabled for beans after CREATE that are not bean-managed and
    // implement the SessionSynchronization interface
    boolean synchronize =
        callContext.getCurrentOperation() != Operation.CREATE
            && callContext.getBeanContext().isSessionSynchronized()
            && txPolicy.isTransactionActive();

    coordinator.registerSessionSynchronization(
        instance, callContext.getBeanContext(), callContext.getPrimaryKey(), synchronize);
  }

  /**
   * SessionSynchronizationCoordinator handles afterBegin, beforeCompletion and afterCompletion
   * callbacks.
   *
   * <p>This class also is responsible for calling releaseInstance after the transaction completes.
   */
  private class SessionSynchronizationCoordinator implements TransactionSynchronization {
    private final Map<Object, Synchronization> registry = new HashMap<Object, Synchronization>();
    private final TransactionPolicy txPolicy;

    private SessionSynchronizationCoordinator(TransactionPolicy txPolicy) {
      this.txPolicy = txPolicy;
    }

    public class Synchronization {
      private final Instance instance;

      private boolean callSessionSynchronization;

      public Synchronization(Instance instance) {
        this.instance = instance;
      }

      public synchronized boolean isCallSessionSynchronization() {
        return callSessionSynchronization;
      }

      public synchronized boolean setCallSessionSynchronization(boolean synchronize) {
        boolean oldValue = this.callSessionSynchronization;
        this.callSessionSynchronization = synchronize;
        return oldValue;
      }
    }

    private void registerSessionSynchronization(
        Instance instance, BeanContext beanContext, Object primaryKey, boolean synchronize) {

      Synchronization synchronization = registry.get(primaryKey);

      if (synchronization == null) {
        synchronization = new Synchronization(instance);
        registry.put(primaryKey, synchronization);
      }

      boolean wasSynchronized = synchronization.setCallSessionSynchronization(synchronize);

      // check if afterBegin has already been invoked or if this is not a session synchronization
      // bean
      if (wasSynchronized || !synchronize) {
        return;
      }

      // Invoke afterBegin
      ThreadContext callContext =
          new ThreadContext(instance.beanContext, instance.primaryKey, Operation.AFTER_BEGIN);
      callContext.setCurrentAllowedStates(null);
      ThreadContext oldCallContext = ThreadContext.enter(callContext);
      try {

        List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, null, Operation.AFTER_BEGIN, interceptors, instance.interceptors);
        interceptorStack.invoke();

      } catch (Exception e) {
        String message =
            "An unexpected system exception occured while invoking the afterBegin method on the SessionSynchronization object";

        // [1] Log the exception or error
        logger.error(message, e);

        // Caller handles transaction rollback and discardInstance

        // [4] throw the java.rmi.RemoteException to the client
        throw new RuntimeException(message, e);
      } finally {
        ThreadContext.exit(oldCallContext);
      }
    }

    public void beforeCompletion() {
      for (Synchronization synchronization : registry.values()) {

        Instance instance = synchronization.instance;

        // don't call beforeCompletion when transaction is marked rollback only
        if (txPolicy.isRollbackOnly()) return;

        // only call beforeCompletion on beans with session synchronization
        if (!synchronization.isCallSessionSynchronization()) continue;

        // Invoke beforeCompletion
        ThreadContext callContext =
            new ThreadContext(
                instance.beanContext, instance.primaryKey, Operation.BEFORE_COMPLETION);
        callContext.setCurrentAllowedStates(null);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
          instance.setInUse(true);

          BeanContext beanContext = instance.beanContext;
          List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
          InterceptorStack interceptorStack =
              new InterceptorStack(
                  instance.bean,
                  null,
                  Operation.BEFORE_COMPLETION,
                  interceptors,
                  instance.interceptors);
          interceptorStack.invoke();

          instance.setInUse(false);
        } catch (InvalidateReferenceException e) {
          // exception has alredy been handled
        } catch (Exception e) {
          String message =
              "An unexpected system exception occured while invoking the beforeCompletion method on the SessionSynchronization object";

          // [1] Log the exception or error
          logger.error(message, e);

          // [2] Mark the transaction for rollback.
          txPolicy.setRollbackOnly(e);

          // [3] Discard the instance
          discardInstance(callContext);

          // [4] throw the java.rmi.RemoteException to the client
          throw new RuntimeException(message, e);
        } finally {
          ThreadContext.exit(oldCallContext);
        }
      }
    }

    public void afterCompletion(Status status) {
      Throwable firstException = null;
      for (Synchronization synchronization : registry.values()) {

        Instance instance = synchronization.instance;

        ThreadContext callContext =
            new ThreadContext(
                instance.beanContext, instance.primaryKey, Operation.AFTER_COMPLETION);
        callContext.setCurrentAllowedStates(null);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
          instance.setInUse(true);
          if (synchronization.isCallSessionSynchronization()) {

            BeanContext beanContext = instance.beanContext;
            List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
            InterceptorStack interceptorStack =
                new InterceptorStack(
                    instance.bean,
                    null,
                    Operation.AFTER_COMPLETION,
                    interceptors,
                    instance.interceptors);
            interceptorStack.invoke(status == Status.COMMITTED);
          }
          instance.setTransaction(null);
          releaseInstance(instance);
        } catch (InvalidateReferenceException inv) {
          // exception has alredy been handled
        } catch (Throwable e) {
          String message =
              "An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object";

          // [1] Log the exception or error
          logger.error(message, e);

          // Transaction is complete so can not be rolled back

          // [3] Discard the instance
          discardInstance(callContext);

          // [4] throw throw first exception to the client
          if (firstException == null) firstException = e;
        } finally {
          ThreadContext.exit(oldCallContext);
        }
      }

      if (firstException != null) {
        throw new RuntimeException(
            "An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object",
            firstException);
      }
    }
  }

  public class StatefulCacheListener implements CacheListener<Instance> {
    public void afterLoad(Instance instance) throws SystemException, ApplicationException {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(instance.beanContext, instance.primaryKey, Operation.ACTIVATE);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method remove =
            instance.bean instanceof SessionBean
                ? SessionBean.class.getMethod("ejbActivate")
                : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                remove,
                Operation.ACTIVATE,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();
      } catch (Throwable callbackException) {
        discardInstance(threadContext);
        handleSystemException(
            threadContext.getTransactionPolicy(), callbackException, threadContext);
      } finally {
        ThreadContext.exit(oldContext);
      }
    }

    public void beforeStore(Instance instance) {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(beanContext, instance.primaryKey, Operation.PASSIVATE);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method passivate =
            instance.bean instanceof SessionBean
                ? SessionBean.class.getMethod("ejbPassivate")
                : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                passivate,
                Operation.PASSIVATE,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();

      } catch (Throwable e) {
        logger.error(
            "An unexpected exception occured while invoking the ejbPassivate method on the Stateful SessionBean instance",
            e);
      } finally {
        ThreadContext.exit(oldContext);
      }
    }

    public void timedOut(Instance instance) {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(beanContext, instance.primaryKey, Operation.PRE_DESTROY);
      threadContext.setCurrentAllowedStates(null);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method remove =
            instance.bean instanceof SessionBean ? SessionBean.class.getMethod("ejbRemove") : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                remove,
                Operation.PRE_DESTROY,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();
      } catch (Throwable e) {
        logger.error(
            "An unexpected exception occured while invoking the ejbRemove method on the timed-out Stateful SessionBean instance",
            e);
      } finally {
        logger.info("Removing the timed-out stateful session bean instance " + instance.primaryKey);
        ThreadContext.exit(oldContext);
      }
    }
  }

  private static class Data {
    private final Index<Method, MethodType> methodIndex;
    private final List<ObjectName> jmxNames = new ArrayList<ObjectName>();

    private Data(Index<Method, MethodType> methodIndex) {
      this.methodIndex = methodIndex;
    }

    public Index<Method, MethodType> getMethodIndex() {
      return methodIndex;
    }
  }
}
Beispiel #7
0
/** @version $Revision$ $Date$ */
public class EjbJarInfoBuilder {

  public static Messages messages = new Messages("org.apache.openejb.util.resources");
  public static Logger logger =
      Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");

  private final List<String> deploymentIds = new ArrayList<>();
  private final List<String> securityRoles = new ArrayList<>();

  public EjbJarInfo buildInfo(final EjbModule jar) throws OpenEJBException {
    deploymentIds.clear();
    securityRoles.clear();

    final Map<String, EjbDeployment> ejbds = jar.getOpenejbJar().getDeploymentsByEjbName();
    final int beansDeployed = jar.getOpenejbJar().getEjbDeploymentCount();
    final int beansInEjbJar = jar.getEjbJar().getEnterpriseBeans().length;

    if (beansInEjbJar != beansDeployed) {

      for (final EnterpriseBean bean : jar.getEjbJar().getEnterpriseBeans()) {
        if (!ejbds.containsKey(bean.getEjbName())) {
          ConfigUtils.logger.warning("conf.0018", bean.getEjbName(), jar.getJarLocation());
        }
      }

      final String message =
          messages.format(
              "conf.0008",
              jar.getJarLocation(),
              String.valueOf(beansInEjbJar),
              String.valueOf(beansDeployed));
      logger.warning(message);
      throw new OpenEJBException(message);
    }

    final Map<String, EnterpriseBeanInfo> infos = new HashMap<String, EnterpriseBeanInfo>();
    final Map<String, EnterpriseBean> items = new HashMap<String, EnterpriseBean>();

    final EjbJarInfo ejbJar = new EjbJarInfo();
    ejbJar.path = jar.getJarLocation();
    ejbJar.moduleUri = jar.getModuleUri();
    ejbJar.moduleId = jar.getModuleId();
    if (jar.getEjbJar() != null && jar.getEjbJar().getModuleName() != null) {
      ejbJar.moduleName = jar.getEjbJar().getModuleName();
    } else {
      ejbJar.moduleName = jar.getModuleId();
    }

    ejbJar.watchedResources.addAll(jar.getWatchedResources());

    ejbJar.properties.putAll(jar.getProperties());
    ejbJar.properties.putAll(jar.getOpenejbJar().getProperties());

    for (final EnterpriseBean bean : jar.getEjbJar().getEnterpriseBeans()) {
      final EnterpriseBeanInfo beanInfo;
      if (bean instanceof SessionBean) {
        beanInfo = initSessionBean((SessionBean) bean, ejbJar, ejbds);
      } else if (bean instanceof EntityBean) {
        beanInfo = initEntityBean((EntityBean) bean, ejbds);
      } else if (bean instanceof MessageDrivenBean) {
        beanInfo = initMessageBean((MessageDrivenBean) bean, ejbds);
      } else {
        throw new OpenEJBException("Unknown bean type: " + bean.getClass().getName());
      }
      ejbJar.enterpriseBeans.add(beanInfo);

      if (deploymentIds.contains(beanInfo.ejbDeploymentId)) {
        final String message =
            messages.format(
                "conf.0100", beanInfo.ejbDeploymentId, jar.getJarLocation(), beanInfo.ejbName);
        logger.warning(message);
        throw new OpenEJBException(message);
      }

      deploymentIds.add(beanInfo.ejbDeploymentId);

      beanInfo.codebase = jar.getJarLocation();
      infos.put(beanInfo.ejbName, beanInfo);
      items.put(beanInfo.ejbName, bean);

      if (bean.getSecurityIdentity() != null) {
        beanInfo.runAs = bean.getSecurityIdentity().getRunAs();

        final EjbDeployment deployment = ejbds.get(beanInfo.ejbName);
        if (deployment != null) {
          for (final RoleMapping mapping : deployment.getRoleMapping()) {
            if (mapping.getRoleName().equals(beanInfo.runAs)) {
              beanInfo.runAsUser = mapping.getPrincipalName();
              break;
            }
          }
        }
      }

      initJndiNames(ejbds, beanInfo);
    }

    if (jar.getEjbJar().getAssemblyDescriptor() != null) {
      initInterceptors(jar, ejbJar);
      initSecurityRoles(jar, ejbJar);
      initMethodPermissions(jar, ejbds, ejbJar);
      initExcludesList(jar, ejbds, ejbJar);
      initMethodTransactions(jar, ejbds, ejbJar);
      initMethodConcurrency(jar, ejbds, ejbJar);
      initApplicationExceptions(jar, ejbJar);

      for (final EnterpriseBeanInfo bean : ejbJar.enterpriseBeans) {
        resolveRoleLinks(bean, items.get(bean.ejbName));
      }
    }

    if (jar.getEjbJar().getRelationships() != null) {
      initRelationships(jar, infos);
    }

    final Beans beans = jar.getBeans();
    if (beans != null) {
      ejbJar.beans = new BeansInfo();
      ejbJar.beans.version = beans.getVersion();
      ejbJar.beans.discoveryMode = beans.getBeanDiscoveryMode();
      if (beans.getScan() != null) {
        for (final Beans.Scan.Exclude exclude : beans.getScan().getExclude()) {
          final ExclusionInfo exclusionInfo = new ExclusionInfo();
          for (final Object config :
              exclude.getIfClassAvailableOrIfClassNotAvailableOrIfSystemProperty()) {
            if (Beans.Scan.Exclude.IfAvailableClassCondition.class.isInstance(config)) {
              exclusionInfo.availableClasses.add(
                  Beans.Scan.Exclude.ClassCondition.class.cast(config).getName());
            } else if (Beans.Scan.Exclude.IfNotAvailableClassCondition.class.isInstance(config)) {
              exclusionInfo.notAvailableClasses.add(
                  Beans.Scan.Exclude.ClassCondition.class.cast(config).getName());
            } else if (Beans.Scan.Exclude.IfSystemProperty.class.isInstance(config)) {
              final Beans.Scan.Exclude.IfSystemProperty systemProperty =
                  Beans.Scan.Exclude.IfSystemProperty.class.cast(config);
              if (systemProperty.getValue() == null) {
                exclusionInfo.systemPropertiesPresence.add(systemProperty.getName());
              } else {
                exclusionInfo.systemProperties.put(
                    systemProperty.getName(), systemProperty.getValue());
              }
            } else {
              throw new IllegalArgumentException("Not supported: " + config);
            }
          }

          final BeansInfo.ExclusionEntryInfo exclusionEntryInfo =
              new BeansInfo.ExclusionEntryInfo();
          exclusionEntryInfo.name = exclude.getName();
          exclusionEntryInfo.exclusion = exclusionInfo;
          ejbJar.beans.excludes.add(exclusionEntryInfo);
        }
      }

      ejbJar.beans.interceptors.addAll(beans.getInterceptors());
      ejbJar.beans.decorators.addAll(beans.getDecorators());
      ejbJar.beans.alternativeClasses.addAll(beans.getAlternativeClasses());
      ejbJar.beans.alternativeStereotypes.addAll(beans.getAlternativeStereotypes());

      ejbJar.beans.duplicatedAlternativeClasses.addAll(
          beans.getDuplicatedAlternatives().getClasses());
      ejbJar.beans.duplicatedAlternativeStereotypes.addAll(
          beans.getDuplicatedAlternatives().getStereotypes());
      ejbJar.beans.duplicatedInterceptors.addAll(beans.getDuplicatedInterceptors());
      ejbJar.beans.duplicatedDecorators.addAll(beans.getDuplicatedDecorators());

      ejbJar.beans.startupClasses.addAll(beans.getStartupBeans());

      final Map<URL, String> discoveryModeByUrl = new HashMap<>();
      if (CompositeBeans.class.isInstance(beans)) {
        discoveryModeByUrl.putAll(CompositeBeans.class.cast(beans).getDiscoveryByUrl());
      } else {
        discoveryModeByUrl.put(null, beans.getBeanDiscoveryMode());
      }
      for (final Map.Entry<URL, List<String>> next : beans.getManagedClasses().entrySet()) {
        final URL key = next.getKey();

        final BeansInfo.BDAInfo bdaInfo = new BeansInfo.BDAInfo();
        bdaInfo.managedClasses.addAll(next.getValue());
        bdaInfo.discoveryMode = discoveryModeByUrl.get(key);
        try {
          bdaInfo.uri = key == null ? null : key.toURI();
        } catch (final URISyntaxException e) {
          bdaInfo.uri = null;
        }
        ejbJar.beans.bdas.add(bdaInfo);
      }
    }

    return ejbJar;
  }

  private void initJndiNames(
      final Map<String, EjbDeployment> ejbds, final EnterpriseBeanInfo info) {
    final EjbDeployment deployment = ejbds.get(info.ejbName);
    if (deployment != null) {
      for (final Jndi jndi : deployment.getJndi()) {
        final JndiNameInfo jndiNameInfo = new JndiNameInfo();
        jndiNameInfo.intrface = jndi.getInterface();
        jndiNameInfo.name = jndi.getName();
        info.jndiNamess.add(jndiNameInfo);
      }
    }
  }

  private void initRelationships(final EjbModule jar, final Map<String, EnterpriseBeanInfo> infos)
      throws OpenEJBException {
    for (final EjbRelation ejbRelation : jar.getEjbJar().getRelationships().getEjbRelation()) {
      final Iterator<EjbRelationshipRole> iterator =
          ejbRelation.getEjbRelationshipRole().iterator();
      final EjbRelationshipRole left = iterator.next();
      final EjbRelationshipRole right = iterator.next();

      // left role info
      final CmrFieldInfo leftCmrFieldInfo = initRelationshipRole(left, right, infos);
      final CmrFieldInfo rightCmrFieldInfo = initRelationshipRole(right, left, infos);
      leftCmrFieldInfo.mappedBy = rightCmrFieldInfo;
      rightCmrFieldInfo.mappedBy = leftCmrFieldInfo;
    }
  }

  private CmrFieldInfo initRelationshipRole(
      final EjbRelationshipRole role,
      final EjbRelationshipRole relatedRole,
      final Map<String, EnterpriseBeanInfo> infos)
      throws OpenEJBException {
    final CmrFieldInfo cmrFieldInfo = new CmrFieldInfo();

    // find the entityBeanInfo info for this role
    final String ejbName = role.getRelationshipRoleSource().getEjbName();
    final EnterpriseBeanInfo enterpriseBeanInfo = infos.get(ejbName);
    if (enterpriseBeanInfo == null) {
      throw new OpenEJBException("Relation role source ejb not found " + ejbName);
    }
    if (!(enterpriseBeanInfo instanceof EntityBeanInfo)) {
      throw new OpenEJBException("Relation role source ejb is not an entity bean " + ejbName);
    }
    final EntityBeanInfo entityBeanInfo = (EntityBeanInfo) enterpriseBeanInfo;
    cmrFieldInfo.roleSource = entityBeanInfo;

    // RoleName: this may be null
    cmrFieldInfo.roleName = role.getEjbRelationshipRoleName();

    cmrFieldInfo.synthetic = role.getCmrField() == null;

    // CmrFieldName: is null for uni-directional relationships
    if (role.getCmrField() != null) {
      cmrFieldInfo.fieldName = role.getCmrField().getCmrFieldName();
      // CollectionType: java.util.Collection or java.util.Set
      if (role.getCmrField().getCmrFieldType() != null) {
        cmrFieldInfo.fieldType = role.getCmrField().getCmrFieldType().toString();
      }

      if (cmrFieldInfo.fieldType == null && relatedRole.getMultiplicity() == Multiplicity.MANY) {
        cmrFieldInfo.fieldType = Collection.class.getName();
      }
    } else {
      final String relatedEjbName = relatedRole.getRelationshipRoleSource().getEjbName();
      final EnterpriseBeanInfo relatedEjb = infos.get(relatedEjbName);
      if (relatedEjb == null) {
        throw new OpenEJBException("Relation role source ejb not found " + relatedEjbName);
      }
      if (!(relatedEjb instanceof EntityBeanInfo)) {
        throw new OpenEJBException(
            "Relation role source ejb is not an entity bean " + relatedEjbName);
      }
      final EntityBeanInfo relatedEntity = (EntityBeanInfo) relatedEjb;

      relatedRole.getRelationshipRoleSource();
      cmrFieldInfo.fieldName =
          relatedEntity.abstractSchemaName + "_" + relatedRole.getCmrField().getCmrFieldName();
      if (relatedRole.getMultiplicity() == Multiplicity.MANY) {
        cmrFieldInfo.fieldType = Collection.class.getName();
      }
    }

    // CascadeDelete
    cmrFieldInfo.cascadeDelete = role.getCascadeDelete();
    // Multiplicity: one or many
    cmrFieldInfo.many = role.getMultiplicity() == Multiplicity.MANY;

    // add the field to the entityBean
    entityBeanInfo.cmrFields.add(cmrFieldInfo);

    return cmrFieldInfo;
  }

  private void initInterceptors(final EjbModule jar, final EjbJarInfo ejbJar)
      throws OpenEJBException {
    if (jar.getEjbJar().getInterceptors().length == 0) {
      return;
    }
    if (jar.getEjbJar().getAssemblyDescriptor() == null) {
      return;
    }
    if (jar.getEjbJar().getAssemblyDescriptor().getInterceptorBinding() == null) {
      return;
    }

    for (final Interceptor s : jar.getEjbJar().getInterceptors()) {
      final InterceptorInfo info = new InterceptorInfo();

      info.clazz = s.getInterceptorClass();

      copyCallbacks(s.getAroundInvoke(), info.aroundInvoke);

      copyCallbacks(s.getPostConstruct(), info.postConstruct);
      copyCallbacks(s.getPreDestroy(), info.preDestroy);

      copyCallbacks(s.getPostActivate(), info.postActivate);
      copyCallbacks(s.getPrePassivate(), info.prePassivate);

      copyCallbacks(s.getAfterBegin(), info.afterBegin);
      copyCallbacks(s.getBeforeCompletion(), info.beforeCompletion);
      copyCallbacks(s.getAfterCompletion(), info.afterCompletion);

      copyCallbacks(s.getAroundTimeout(), info.aroundTimeout);

      ejbJar.interceptors.add(info);
    }

    for (final InterceptorBinding binding :
        jar.getEjbJar().getAssemblyDescriptor().getInterceptorBinding()) {
      final InterceptorBindingInfo info = new InterceptorBindingInfo();
      info.ejbName = binding.getEjbName();
      info.excludeClassInterceptors = binding.getExcludeClassInterceptors();
      info.excludeDefaultInterceptors = binding.getExcludeDefaultInterceptors();
      info.interceptors.addAll(binding.getInterceptorClass());
      if (binding.getInterceptorOrder() != null) {
        info.interceptorOrder.addAll(binding.getInterceptorOrder().getInterceptorClass());
      }

      info.method = toInfo(binding.getMethod());
      info.className = binding.getClassName();
      ejbJar.interceptorBindings.add(info);
    }
  }

  private void initMethodTransactions(
      final EjbModule jar, final Map ejbds, final EjbJarInfo ejbJarInfo) {
    final List<ContainerTransaction> containerTransactions =
        jar.getEjbJar().getAssemblyDescriptor().getContainerTransaction();
    for (final ContainerTransaction cTx : containerTransactions) {
      final MethodTransactionInfo info = new MethodTransactionInfo();

      info.description = cTx.getDescription();
      info.transAttribute = cTx.getTransAttribute().toString();
      info.methods.addAll(getMethodInfos(cTx.getMethod(), ejbds));
      ejbJarInfo.methodTransactions.add(info);
    }
  }

  private void initMethodConcurrency(
      final EjbModule jar, final Map ejbds, final EjbJarInfo ejbJarInfo) {
    final List<ContainerConcurrency> containerConcurrency =
        jar.getEjbJar().getAssemblyDescriptor().getContainerConcurrency();
    for (final ContainerConcurrency att : containerConcurrency) {
      final MethodConcurrencyInfo info = new MethodConcurrencyInfo();

      info.description = att.getDescription();
      if (att.getLock() != null) {
        info.concurrencyAttribute = att.getLock().toString();
      }
      info.accessTimeout = toInfo(att.getAccessTimeout());

      info.methods.addAll(getMethodInfos(att.getMethod(), ejbds));
      ejbJarInfo.methodConcurrency.add(info);
    }
  }

  private void copyConcurrentMethods(
      final SessionBean bean, final EjbJarInfo ejbJarInfo, final Map ejbds) {
    for (final ConcurrentMethod method : bean.getConcurrentMethod()) {
      final MethodConcurrencyInfo info = new MethodConcurrencyInfo();

      if (method.getLock() != null) {
        info.concurrencyAttribute = method.getLock().toString();
      }
      info.accessTimeout = toInfo(method.getAccessTimeout());

      final Method m = new Method(bean.getEjbName(), null, method.getMethod().getMethodName());
      m.setMethodParams(method.getMethod().getMethodParams());
      info.methods.add(getMethodInfo(m, ejbds));
      ejbJarInfo.methodConcurrency.add(info);
    }
  }

  private void copySchedules(
      final List<Timer> timers, final List<MethodScheduleInfo> scheduleInfos) {
    final Map<NamedMethod, MethodScheduleInfo> methodScheduleInfoMap =
        new HashMap<NamedMethod, MethodScheduleInfo>();
    for (final Timer timer : timers) {
      final NamedMethod timeoutMethod = timer.getTimeoutMethod();
      MethodScheduleInfo methodScheduleInfo = methodScheduleInfoMap.get(timer.getTimeoutMethod());
      if (methodScheduleInfo == null) {
        methodScheduleInfo = new MethodScheduleInfo();
        methodScheduleInfoMap.put(timeoutMethod, methodScheduleInfo);
        methodScheduleInfo.method = toInfo(timeoutMethod);
      }
      final ScheduleInfo scheduleInfo = new ScheduleInfo();
      // Copy TimerSchedule
      final TimerSchedule timerSchedule = timer.getSchedule();
      if (timerSchedule != null) {
        scheduleInfo.second = timerSchedule.getSecond();
        scheduleInfo.minute = timerSchedule.getMinute();
        scheduleInfo.hour = timerSchedule.getHour();
        scheduleInfo.dayOfWeek = timerSchedule.getDayOfWeek();
        scheduleInfo.dayOfMonth = timerSchedule.getDayOfMonth();
        scheduleInfo.month = timerSchedule.getMonth();
        scheduleInfo.year = timerSchedule.getYear();
      }
      // Copy other attributes
      scheduleInfo.timezone = timer.getTimezone();
      if (timer.getStart() != null) {
        scheduleInfo.start = timer.getStart().toGregorianCalendar().getTime();
      }
      if (timer.getEnd() != null) {
        scheduleInfo.end = timer.getEnd().toGregorianCalendar().getTime();
      }

      scheduleInfo.info = timer.getInfo();
      if (timer.getPersistent() != null) {
        scheduleInfo.persistent = timer.getPersistent();
      }

      methodScheduleInfo.schedules.add(scheduleInfo);
    }
    scheduleInfos.addAll(methodScheduleInfoMap.values());
  }

  private void initApplicationExceptions(final EjbModule jar, final EjbJarInfo ejbJarInfo) {
    for (final ApplicationException applicationException :
        jar.getEjbJar().getAssemblyDescriptor().getApplicationException()) {
      final ApplicationExceptionInfo info = new ApplicationExceptionInfo();
      info.exceptionClass = applicationException.getExceptionClass();
      info.rollback = applicationException.isRollback();
      info.inherited = applicationException.isInherited();
      ejbJarInfo.applicationException.add(info);
    }
  }

  private void initSecurityRoles(final EjbModule jar, final EjbJarInfo ejbJarInfo) {

    final List<SecurityRole> roles = jar.getEjbJar().getAssemblyDescriptor().getSecurityRole();

    for (final SecurityRole sr : roles) {
      final SecurityRoleInfo info = new SecurityRoleInfo();

      info.description = sr.getDescription();
      info.roleName = sr.getRoleName();

      if (securityRoles.contains(sr.getRoleName())) {
        ConfigUtils.logger.warning("conf.0102", jar.getJarLocation(), sr.getRoleName());
      } else {
        securityRoles.add(sr.getRoleName());
      }
      ejbJarInfo.securityRoles.add(info);
    }
  }

  private void initMethodPermissions(
      final EjbModule jar, final Map ejbds, final EjbJarInfo ejbJarInfo) {

    final List<MethodPermission> methodPermissions =
        jar.getEjbJar().getAssemblyDescriptor().getMethodPermission();

    for (final MethodPermission mp : methodPermissions) {
      final MethodPermissionInfo info = new MethodPermissionInfo();

      info.description = mp.getDescription();
      info.roleNames.addAll(mp.getRoleName());
      info.methods.addAll(getMethodInfos(mp.getMethod(), ejbds));
      info.unchecked = mp.getUnchecked();

      ejbJarInfo.methodPermissions.add(info);
    }
  }

  private void initExcludesList(final EjbModule jar, final Map ejbds, final EjbJarInfo ejbJarInfo) {

    final ExcludeList methodPermissions = jar.getEjbJar().getAssemblyDescriptor().getExcludeList();

    for (final Method excludedMethod : methodPermissions.getMethod()) {
      ejbJarInfo.excludeList.add(getMethodInfo(excludedMethod, ejbds));
    }
  }

  private void resolveRoleLinks(final EnterpriseBeanInfo bean, final JndiConsumer item) {
    if (!(item instanceof RemoteBean)) {
      return;
    }

    final RemoteBean rb = (RemoteBean) item;

    final List<SecurityRoleRef> refs = rb.getSecurityRoleRef();
    for (final SecurityRoleRef ref : refs) {
      final SecurityRoleReferenceInfo info = new SecurityRoleReferenceInfo();

      info.description = ref.getDescription();
      info.roleLink = ref.getRoleLink();
      info.roleName = ref.getRoleName();

      if (info.roleLink == null) {
        info.roleLink = info.roleName;
      }
      bean.securityRoleReferences.add(info);
    }
  }

  private List<MethodInfo> getMethodInfos(final List<Method> ms, final Map ejbds) {
    if (ms == null) {
      return Collections.emptyList();
    }

    final List<MethodInfo> mi = new ArrayList<MethodInfo>(ms.size());
    for (final Method method : ms) {
      final MethodInfo methodInfo = getMethodInfo(method, ejbds);
      mi.add(methodInfo);
    }

    return mi;
  }

  private MethodInfo getMethodInfo(final Method method, final Map ejbds) {
    final MethodInfo methodInfo = new MethodInfo();

    final EjbDeployment d = (EjbDeployment) ejbds.get(method.getEjbName());

    methodInfo.description = method.getDescription();
    methodInfo.ejbDeploymentId = d == null ? null : d.getDeploymentId();
    methodInfo.ejbName = method.getEjbName();
    methodInfo.methodIntf =
        method.getMethodIntf() == null ? null : method.getMethodIntf().toString();
    methodInfo.methodName = method.getMethodName();
    if (methodInfo.methodName == null || methodInfo.methodName.equals("")) {
      methodInfo.methodName = "*";
    }
    methodInfo.className = method.getClassName();
    if (methodInfo.className == null || methodInfo.className.equals("")) {
      methodInfo.className = "*";
    }

    final MethodParams mp = method.getMethodParams();
    if (mp != null) {
      methodInfo.methodParams = mp.getMethodParam();
    }
    return methodInfo;
  }

  private EnterpriseBeanInfo initSessionBean(
      final SessionBean s, final EjbJarInfo ejbJar, final Map m) throws OpenEJBException {
    EnterpriseBeanInfo bean = null;

    if (s.getSessionType() == SessionType.STATEFUL) {
      bean = new StatefulBeanInfo();
      bean.passivable = s.getPassivationCapable();
      final StatefulBeanInfo stateful = (StatefulBeanInfo) bean;

      copyCallbacks(s.getPostActivate(), stateful.postActivate);
      copyCallbacks(s.getPrePassivate(), stateful.prePassivate);

      copyCallbacks(s.getAfterBegin(), stateful.afterBegin);
      copyCallbacks(s.getBeforeCompletion(), stateful.beforeCompletion);
      copyCallbacks(s.getAfterCompletion(), stateful.afterCompletion);

      for (final InitMethod initMethod : s.getInitMethod()) {
        final InitMethodInfo init = new InitMethodInfo();
        init.beanMethod = toInfo(initMethod.getBeanMethod());
        init.createMethod = toInfo(initMethod.getCreateMethod());
        stateful.initMethods.add(init);
      }

      for (final RemoveMethod removeMethod : s.getRemoveMethod()) {
        final RemoveMethodInfo remove = new RemoveMethodInfo();
        remove.beanMethod = toInfo(removeMethod.getBeanMethod());
        remove.retainIfException = removeMethod.getRetainIfException();
        stateful.removeMethods.add(remove);
      }

      copyConcurrentMethods(s, ejbJar, m);

    } else if (s.getSessionType() == SessionType.MANAGED) {
      bean = new ManagedBeanInfo();
      final ManagedBeanInfo managed = (ManagedBeanInfo) bean;
      if (s
          instanceof
          ManagedBean) { // this way we support managed beans in ejb-jar.xml (not in the spec but
        // can be useful)
        managed.hidden = ((ManagedBean) s).isHidden();
      } else {
        managed.hidden = true;
      }

      copyCallbacks(s.getPostActivate(), managed.postActivate);
      copyCallbacks(s.getPrePassivate(), managed.prePassivate);

      for (final RemoveMethod removeMethod : s.getRemoveMethod()) {
        final RemoveMethodInfo remove = new RemoveMethodInfo();
        remove.beanMethod = toInfo(removeMethod.getBeanMethod());
        remove.retainIfException = removeMethod.getRetainIfException();
        managed.removeMethods.add(remove);
      }

    } else if (s.getSessionType() == SessionType.SINGLETON) {
      bean = new SingletonBeanInfo();
      final ConcurrencyManagementType type = s.getConcurrencyManagementType();
      bean.concurrencyType =
          type != null ? type.toString() : ConcurrencyManagementType.CONTAINER.toString();
      bean.loadOnStartup = s.getInitOnStartup();

      copyCallbacks(s.getAroundTimeout(), bean.aroundTimeout);
      copySchedules(s.getTimer(), bean.methodScheduleInfos);
      // See JndiEncInfoBuilder.buildDependsOnRefs for processing of DependsOn
      // bean.dependsOn.addAll(s.getDependsOn());

      copyConcurrentMethods(s, ejbJar, m);
    } else {
      bean = new StatelessBeanInfo();
      copySchedules(s.getTimer(), bean.methodScheduleInfos);
    }

    if (s.getSessionType() != SessionType.STATEFUL) {
      copyCallbacks(s.getAroundTimeout(), bean.aroundTimeout);
    }

    bean.localbean = s.getLocalBean() != null;

    bean.timeoutMethod = toInfo(s.getTimeoutMethod());

    copyCallbacks(s.getAroundInvoke(), bean.aroundInvoke);
    copyCallbacks(s.getPostConstruct(), bean.postConstruct);
    copyCallbacks(s.getPreDestroy(), bean.preDestroy);

    copyAsynchronous(s.getAsyncMethod(), bean.asynchronous);
    bean.asynchronousClasses.addAll(s.getAsynchronousClasses());

    final EjbDeployment d = (EjbDeployment) m.get(s.getEjbName());
    if (d == null) {
      throw new OpenEJBException(
          "No deployment information in openejb-jar.xml for bean "
              + s.getEjbName()
              + ". Please redeploy the jar");
    }
    bean.ejbDeploymentId = d.getDeploymentId();
    bean.containerId = d.getContainerId();

    final Icon icon = s.getIcon();
    bean.largeIcon = icon == null ? null : icon.getLargeIcon();
    bean.smallIcon = icon == null ? null : icon.getSmallIcon();
    bean.description = s.getDescription();
    bean.displayName = s.getDisplayName();
    bean.ejbClass = s.getEjbClass();
    bean.ejbName = s.getEjbName();
    bean.home = s.getHome();
    bean.remote = s.getRemote();
    bean.localHome = s.getLocalHome();
    bean.local = s.getLocal();
    bean.proxy = s.getProxy();
    bean.parents.addAll(s.getParents());
    bean.businessLocal.addAll(s.getBusinessLocal());
    bean.businessRemote.addAll(s.getBusinessRemote());
    final TransactionType txType = s.getTransactionType();
    bean.transactionType =
        txType != null ? txType.toString() : TransactionType.CONTAINER.toString();
    bean.serviceEndpoint = s.getServiceEndpoint();
    bean.properties.putAll(d.getProperties());

    bean.statefulTimeout = toInfo(s.getStatefulTimeout());

    bean.restService = s.isRestService() && !(s instanceof StatefulBean);

    return bean;
  }

  private void copyAsynchronous(
      final List<AsyncMethod> methods, final List<NamedMethodInfo> methodInfos) {
    for (final AsyncMethod asyncMethod : methods) {
      final NamedMethodInfo info = new NamedMethodInfo();
      info.methodName = asyncMethod.getMethodName();
      if (asyncMethod.getMethodParams() != null) {
        info.methodParams = asyncMethod.getMethodParams().getMethodParam();
      }
      methodInfos.add(info);
    }
  }

  private TimeoutInfo toInfo(final Timeout timeout) {
    if (timeout == null) {
      return null;
    }

    final TimeoutInfo accessTimeout = new TimeoutInfo();
    accessTimeout.time = timeout.getTimeout();
    accessTimeout.unit = timeout.getUnit().toString();
    return accessTimeout;
  }

  private EnterpriseBeanInfo initMessageBean(final MessageDrivenBean mdb, final Map m)
      throws OpenEJBException {
    final MessageDrivenBeanInfo bean = new MessageDrivenBeanInfo();

    bean.timeoutMethod = toInfo(mdb.getTimeoutMethod());
    copyCallbacks(mdb.getAroundTimeout(), bean.aroundTimeout);

    copyCallbacks(mdb.getAroundInvoke(), bean.aroundInvoke);
    copyCallbacks(mdb.getPostConstruct(), bean.postConstruct);
    copyCallbacks(mdb.getPreDestroy(), bean.preDestroy);

    copySchedules(mdb.getTimer(), bean.methodScheduleInfos);

    final EjbDeployment d = (EjbDeployment) m.get(mdb.getEjbName());
    if (d == null) {
      throw new OpenEJBException(
          "No deployment information in openejb-jar.xml for bean "
              + mdb.getEjbName()
              + ". Please redeploy the jar");
    }
    bean.ejbDeploymentId = d.getDeploymentId();
    bean.containerId = d.getContainerId();

    final Icon icon = mdb.getIcon();
    bean.largeIcon = icon == null ? null : icon.getLargeIcon();
    bean.smallIcon = icon == null ? null : icon.getSmallIcon();
    bean.description = mdb.getDescription();
    bean.displayName = mdb.getDisplayName();
    bean.ejbClass = mdb.getEjbClass();
    bean.ejbName = mdb.getEjbName();
    final TransactionType txType = mdb.getTransactionType();
    bean.transactionType =
        txType != null ? txType.toString() : TransactionType.CONTAINER.toString();
    bean.properties.putAll(d.getProperties());

    if (mdb.getMessagingType() != null) {
      bean.mdbInterface = mdb.getMessagingType();
    } else {
      bean.mdbInterface = "javax.jms.MessageListener";
    }

    final ResourceLink resourceLink = d.getResourceLink("openejb/destination");
    if (resourceLink != null) {
      bean.destinationId = resourceLink.getResId();
    }

    if (mdb.getMessageDestinationType() != null) {
      bean.activationProperties.put("destinationType", mdb.getMessageDestinationType());
    }

    final ActivationConfig activationConfig = mdb.getActivationConfig();
    if (activationConfig != null) {
      for (final ActivationConfigProperty property :
          activationConfig.getActivationConfigProperty()) {
        final String name = property.getActivationConfigPropertyName();
        final String value = property.getActivationConfigPropertyValue();
        bean.activationProperties.put(name, value);
      }
    }

    return bean;
  }

  private NamedMethodInfo toInfo(final NamedMethod method) {
    if (method == null) {
      return null;
    }

    final NamedMethodInfo info = new NamedMethodInfo();

    info.methodName = method.getMethodName();

    if (method.getMethodParams() != null) {
      info.methodParams = method.getMethodParams().getMethodParam();
    }

    return info;
  }

  private void copyCallbacks(
      final List<? extends CallbackMethod> from, final List<CallbackInfo> to) {
    for (final CallbackMethod callback : from) {
      final CallbackInfo info = new CallbackInfo();
      info.className = callback.getClassName();
      info.method = callback.getMethodName();
      to.add(info);
    }
  }

  private EnterpriseBeanInfo initEntityBean(final EntityBean e, final Map m)
      throws OpenEJBException {
    final EntityBeanInfo bean = new EntityBeanInfo();

    final EjbDeployment d = (EjbDeployment) m.get(e.getEjbName());
    if (d == null) {
      throw new OpenEJBException(
          "No deployment information in openejb-jar.xml for bean "
              + e.getEjbName()
              + ". Please redeploy the jar");
    }
    bean.ejbDeploymentId = d.getDeploymentId();
    bean.containerId = d.getContainerId();

    final Icon icon = e.getIcon();
    bean.largeIcon = icon == null ? null : icon.getLargeIcon();
    bean.smallIcon = icon == null ? null : icon.getSmallIcon();
    bean.description = e.getDescription();
    bean.displayName = e.getDisplayName();
    bean.ejbClass = e.getEjbClass();
    bean.abstractSchemaName = e.getAbstractSchemaName();
    bean.ejbName = e.getEjbName();
    bean.home = e.getHome();
    bean.remote = e.getRemote();
    bean.localHome = e.getLocalHome();
    bean.local = e.getLocal();
    bean.transactionType = "Container";

    bean.primKeyClass = e.getPrimKeyClass();
    bean.primKeyField = e.getPrimkeyField();
    bean.persistenceType = e.getPersistenceType().toString();
    bean.reentrant = String.valueOf(e.getReentrant());
    bean.properties.putAll(d.getProperties());

    final CmpVersion cmpVersion = e.getCmpVersion();
    if (e.getPersistenceType() == PersistenceType.CONTAINER) {
      if (cmpVersion != null && cmpVersion == CmpVersion.CMP1) {
        bean.cmpVersion = 1;
      } else {
        bean.cmpVersion = 2;
      }
    }

    final List<CmpField> cmpFields = e.getCmpField();
    for (final CmpField cmpField : cmpFields) {
      bean.cmpFieldNames.add(cmpField.getFieldName());
    }

    if (bean.persistenceType.equalsIgnoreCase("Container")) {
      for (final Query q : e.getQuery()) {
        final QueryInfo query = new QueryInfo();
        query.queryStatement = q.getEjbQl().trim();

        final MethodInfo method = new MethodInfo();
        method.ejbName = bean.ejbName;
        method.className = "*";

        final QueryMethod qm = q.getQueryMethod();
        method.methodName = qm.getMethodName();
        if (qm.getMethodParams() != null) {
          method.methodParams = qm.getMethodParams().getMethodParam();
        }
        query.method = method;
        final ResultTypeMapping resultType = q.getResultTypeMapping();
        if (ResultTypeMapping.REMOTE.equals(resultType)) {
          query.remoteResultType = true;
        }
        bean.queries.add(query);
      }

      for (final org.apache.openejb.jee.oejb3.Query q : d.getQuery()) {
        final QueryInfo query = new QueryInfo();
        query.description = q.getDescription();
        query.queryStatement = q.getObjectQl().trim();

        final MethodInfo method = new MethodInfo();
        method.ejbName = bean.ejbName;
        method.className = "*";
        final org.apache.openejb.jee.oejb3.QueryMethod qm = q.getQueryMethod();
        method.methodName = qm.getMethodName();
        if (qm.getMethodParams() != null) {
          method.methodParams = qm.getMethodParams().getMethodParam();
        }
        query.method = method;
        bean.queries.add(query);
      }
    }
    return bean;
  }
}
Beispiel #8
0
/** @version $Rev:$ $Date:$ */
public class OpenEJBLifecycle implements ContainerLifecycle {
  public static final ThreadLocal<AppInfo> CURRENT_APP_INFO = new ThreadLocal<AppInfo>();

  // Logger instance
  private static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB_CDI, OpenEJBLifecycle.class);

  /** Discover bean classes */
  protected ScannerService scannerService;

  protected final ContextsService contextsService;

  /** Deploy discovered beans */
  private final BeansDeployer deployer;

  /** Using for lookup operations */
  private final JNDIService jndiService;

  /** Root container. */
  private final BeanManagerImpl beanManager;

  private final WebBeansContext webBeansContext;
  /** Manages unused conversations */
  public OpenEJBLifecycle(final WebBeansContext webBeansContext) {
    this.webBeansContext = webBeansContext;

    this.beanManager = webBeansContext.getBeanManagerImpl();
    this.deployer = new BeansDeployer(webBeansContext);
    this.jndiService = webBeansContext.getService(JNDIService.class);
    this.scannerService = webBeansContext.getScannerService();
    this.contextsService = webBeansContext.getContextsService();
  }

  @Override
  public BeanManager getBeanManager() {
    return this.beanManager;
  }

  @Override
  public void startApplication(final Object startupObject) {
    if (ServletContextEvent.class.isInstance(startupObject)) {
      startServletContext(
          ServletContext.class.cast(
              getServletContext(startupObject))); // TODO: check it is relevant
      return;
    } else if (!StartupObject.class.isInstance(startupObject)) {
      logger.debug("startupObject is not of StartupObject type; ignored");
      return;
    }

    final StartupObject stuff = (StartupObject) startupObject;
    final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();

    // Initalize Application Context
    logger.info("OpenWebBeans Container is starting...");

    final long begin = System.currentTimeMillis();

    try {
      Thread.currentThread().setContextClassLoader(stuff.getClassLoader());

      final AppContext appContext = stuff.getAppContext();
      if (stuff.getWebContext()
          == null) { // do it before any other things to keep our singleton finder working
        appContext.setWebBeansContext(webBeansContext);
      }

      // Load all plugins
      webBeansContext.getPluginLoader().startUp();

      // Get Plugin
      final CdiPlugin cdiPlugin = (CdiPlugin) webBeansContext.getPluginLoader().getEjbPlugin();

      cdiPlugin.setClassLoader(stuff.getClassLoader());
      cdiPlugin.setWebBeansContext(webBeansContext);

      // Configure EJB Deployments
      cdiPlugin.configureDeployments(stuff.getBeanContexts());

      // Resournce Injection Service
      final CdiResourceInjectionService injectionService =
          (CdiResourceInjectionService) webBeansContext.getService(ResourceInjectionService.class);
      // todo use startupObject allDeployments to find Comp in priority (otherwise we can keep N
      // times comps and loose time at injection time
      injectionService.setAppContext(
          stuff.getAppContext(),
          stuff.getBeanContexts() != null
              ? stuff.getBeanContexts()
              : Collections.<BeanContext>emptyList());

      // Deploy the beans
      CdiScanner cdiScanner = null;
      try {
        // Scanning process
        logger.debug("Scanning classpaths for beans artifacts.");

        if (CdiScanner.class.isInstance(scannerService)) {
          cdiScanner = CdiScanner.class.cast(scannerService);
          cdiScanner.setContext(webBeansContext);
          cdiScanner.init(startupObject);
        } else {
          cdiScanner = new CdiScanner();
          cdiScanner.setContext(webBeansContext);
          cdiScanner.init(startupObject);
        }

        // Scan
        this.scannerService.scan();

        // just to let us write custom CDI Extension using our internals easily
        CURRENT_APP_INFO.set(stuff.getAppInfo());

        addInternalBeans(); // before next event which can register custom beans (JAX-RS)
        SystemInstance.get().fireEvent(new WebBeansContextBeforeDeploy(webBeansContext));

        // Deploy bean from XML. Also configures deployments, interceptors, decorators.
        deployer.deploy(scannerService);
        contextsService.init(
            startupObject); // fire app event and also starts SingletonContext and
                            // ApplicationContext
      } catch (final Exception e1) {
        SystemInstance.get()
            .getComponent(Assembler.class)
            .logger
            .error("CDI Beans module deployment failed", e1);
        throw new OpenEJBRuntimeException(e1);
      } finally {
        CURRENT_APP_INFO.remove();
      }

      final Collection<Class<?>> ejbs = new ArrayList<>(stuff.getBeanContexts().size());
      for (final BeanContext bc : stuff.getBeanContexts()) {
        final CdiEjbBean cdiEjbBean = bc.get(CdiEjbBean.class);
        if (cdiEjbBean == null) {
          continue;
        }

        ejbs.add(bc.getManagedClass());

        if (AbstractProducer.class.isInstance(cdiEjbBean)) {
          AbstractProducer.class
              .cast(cdiEjbBean)
              .defineInterceptorStack(
                  cdiEjbBean, cdiEjbBean.getAnnotatedType(), cdiEjbBean.getWebBeansContext());
        }
        bc.mergeOWBAndOpenEJBInfo();
        bc.set(
            InterceptorResolutionService.BeanInterceptorInfo.class,
            InjectionTargetImpl.class.cast(cdiEjbBean.getInjectionTarget()).getInterceptorInfo());
        cdiEjbBean.initInternals();
      }

      // Start actual starting on sub-classes
      if (beanManager instanceof WebappBeanManager) {
        ((WebappBeanManager) beanManager).afterStart();
      }

      for (final Class<?> clazz : cdiScanner.getStartupClasses()) {
        if (ejbs.contains(clazz)) {
          continue;
        }
        starts(beanManager, clazz);
      }
    } finally {
      Thread.currentThread().setContextClassLoader(oldCl);

      // cleanup threadlocal used to enrich cdi context manually
      OptimizedLoaderService.ADDITIONAL_EXTENSIONS.remove();
    }

    logger.info(
        "OpenWebBeans Container has started, it took {0} ms.",
        Long.toString(System.currentTimeMillis() - begin));
  }

  private void addInternalBeans() {
    beanManager.getInjectionResolver().clearCaches();

    if (!hasBean(beanManager, HttpServletRequest.class)) {
      beanManager.addInternalBean(new HttpServletRequestBean(webBeansContext));
    }
    if (!hasBean(beanManager, HttpSession.class)) {
      beanManager.addInternalBean(
          new InternalBean<>(webBeansContext, HttpSession.class, HttpSession.class));
    }
    if (!hasBean(beanManager, ServletContext.class)) {
      beanManager.addInternalBean(
          new InternalBean<>(webBeansContext, ServletContext.class, ServletContext.class));
    }

    beanManager
        .getInjectionResolver()
        .clearCaches(); // hasBean() usage can have cached several things
  }

  private static boolean hasBean(final BeanManagerImpl beanManagerImpl, final Class<?> type) {
    return !beanManagerImpl.getInjectionResolver().implResolveByType(false, type).isEmpty();
  }

  private void starts(final BeanManager beanManager, final Class<?> clazz) {
    final Bean<?> bean = beanManager.resolve(beanManager.getBeans(clazz));
    if (!beanManager.isNormalScope(bean.getScope())) {
      throw new IllegalStateException(
          "Only normal scoped beans can use @Startup - likely @ApplicationScoped");
    }

    final CreationalContext<Object> creationalContext = beanManager.createCreationalContext(null);
    beanManager.getReference(bean, clazz, creationalContext).toString();
    // don't release now, will be done by the context - why we restrict it to normal scoped beans
  }

  @Override
  public void stopApplication(final Object endObject) {
    logger.debug("OpenWebBeans Container is stopping.");

    try {
      // Fire shut down
      if (WebappBeanManager.class.isInstance(beanManager)) {
        WebappBeanManager.class.cast(beanManager).beforeStop();
      }

      webBeansContext.getContextsService().endContext(RequestScoped.class, endObject);
      webBeansContext.getContextsService().endContext(ConversationScoped.class, endObject);
      webBeansContext.getContextsService().endContext(SessionScoped.class, endObject);
      webBeansContext.getContextsService().endContext(ApplicationScoped.class, endObject);
      webBeansContext.getContextsService().endContext(Singleton.class, endObject);

      // clean up the EL caches after each request
      ELContextStore elStore = ELContextStore.getInstance(false);
      if (elStore != null) {
        elStore.destroyELContextStore();
      }

      this.beanManager.fireEvent(new BeforeShutdownImpl(), true);

      // this will now even destroy the ExtensionBeans and other internal stuff
      this.contextsService.destroy(endObject);

      // Unbind BeanManager
      if (jndiService != null) {
        jndiService.unbind(WebBeansConstants.WEB_BEANS_MANAGER_JNDI_NAME);
      }

      // Free all plugin resources
      ((CdiPlugin) webBeansContext.getPluginLoader().getEjbPlugin()).clearProxies();
      webBeansContext.getPluginLoader().shutDown();

      // Clear extensions
      webBeansContext.getExtensionLoader().clear();

      // Delete Resolutions Cache
      beanManager.getInjectionResolver().clearCaches();

      // Delete AnnotateTypeCache
      webBeansContext.getAnnotatedElementFactory().clear();

      // After Stop
      // Clear the resource injection service
      final ResourceInjectionService injectionServices =
          webBeansContext.getService(ResourceInjectionService.class);
      if (injectionServices != null) {
        injectionServices.clear();
      }

      // Comment out for commit OWB-502
      // ContextFactory.cleanUpContextFactory();

      CdiAppContextsService.class.cast(contextsService).removeThreadLocals();

      WebBeansFinder.clearInstances(WebBeansUtil.getCurrentClassLoader());

      // Clear BeanManager
      this.beanManager.clear();

      // Clear singleton list
      WebBeansFinder.clearInstances(WebBeansUtil.getCurrentClassLoader());

    } catch (final Exception e) {
      logger.error("An error occured while stopping the container.", e);
    }
  }

  /** @return the scannerService */
  protected ScannerService getScannerService() {
    return scannerService;
  }

  /** @return the contextsService */
  public ContextsService getContextService() {
    return contextsService;
  }

  /** @return the jndiService */
  protected JNDIService getJndiService() {
    return jndiService;
  }

  @Override
  public void initApplication(final Properties properties) {
    // no-op
  }

  public void startServletContext(final ServletContext servletContext) {
    initializeServletContext(servletContext, webBeansContext);
  }

  public static void initializeServletContext(
      final ServletContext servletContext, final WebBeansContext context) {
    if (context == null || !context.getBeanManagerImpl().isInUse()) {
      return;
    }

    final ELAdaptor elAdaptor = context.getService(ELAdaptor.class);
    final ELResolver resolver = elAdaptor.getOwbELResolver();
    // Application is configured as JSP
    if (context.getOpenWebBeansConfiguration().isJspApplication()) {
      logger.debug("Application is configured as JSP. Adding EL Resolver.");

      setJspELFactory(servletContext, resolver);
    }

    // Add BeanManager to the 'javax.enterprise.inject.spi.BeanManager' servlet context attribute
    servletContext.setAttribute(BeanManager.class.getName(), context.getBeanManagerImpl());
  }

  /** On Tomcat we need to sometimes force a class load to get our hands on the JspFactory */
  private static void setJspELFactory(ServletContext startupObject, ELResolver resolver) {
    JspFactory factory = JspFactory.getDefaultFactory();
    if (factory == null) {
      try {
        try {
          Class.forName("org.apache.jasper.servlet.JasperInitializer");
        } catch (final Throwable th) {
          Class.forName("org.apache.jasper.compiler.JspRuntimeContext");
        }
        factory = JspFactory.getDefaultFactory();
      } catch (Exception e) {
        // ignore
      }
    }

    if (factory != null) {
      JspApplicationContext applicationCtx = factory.getJspApplicationContext(startupObject);
      applicationCtx.addELResolver(resolver);
    } else {
      logger.debug("Default JSPFactroy instance has not found. Skipping OWB JSP handling");
    }
  }

  /**
   * Returns servlet context otherwise throws exception.
   *
   * @param object object
   * @return servlet context
   */
  private Object getServletContext(Object object) {
    if (ServletContextEvent.class.isInstance(object)) {
      object = ServletContextEvent.class.cast(object).getServletContext();
      return object;
    }
    return object;
  }

  public static class InternalBean<T> extends BuiltInOwbBean<T> {
    private final String id;

    protected InternalBean(
        final WebBeansContext webBeansContext, final Class<T> api, final Class<?> type) {
      super(
          webBeansContext,
          WebBeansType.MANAGED,
          api,
          new SimpleProducerFactory<T>(
              new ProviderBasedProducer<>(
                  webBeansContext,
                  type,
                  new OpenEJBComponentProvider(webBeansContext, type),
                  false)));
      this.id = "openejb#container#" + api.getName();
    }

    @Override
    public boolean isPassivationCapable() {
      return true;
    }

    @Override
    protected String providedId() {
      return id;
    }

    @Override
    public Class<?> proxyableType() {
      return null;
    }
  }

  public static class HttpServletRequestBean extends InternalBean<HttpServletRequest> {
    private final Set<Type> types;

    protected HttpServletRequestBean(final WebBeansContext webBeansContext) {
      super(webBeansContext, HttpServletRequest.class, HttpServletRequest.class);
      this.types =
          new HashSet<>(); // here we need 2 types (+Object) otherwise decoratione etc fails
      this.types.add(HttpServletRequest.class);
      this.types.add(ServletRequest.class);
      this.types.add(Object.class);
    }

    @Override
    public Set<Type> getTypes() {
      return types;
    }
  }

  private static class OpenEJBComponentProvider<T> implements Provider<T>, Serializable {
    private Class<?> type;
    private transient WebBeansContext webBeansContext;

    public OpenEJBComponentProvider(final WebBeansContext webBeansContext, final Class<?> type) {
      this.webBeansContext = webBeansContext;
      this.type = type;
    }

    @Override
    public T get() {
      if (webBeansContext == null) {
        webBeansContext = WebBeansContext.currentInstance();
      }
      return (T) SystemInstance.get().getComponent(type);
    }

    Object readResolve() throws ObjectStreamException {
      return get();
    }
  }
}
@SuppressWarnings({"UnusedDeclaration"})
public class OpenEJBNamingContextListener implements LifecycleListener, PropertyChangeListener {
  private static final Logger logger =
      Logger.getInstance(
          LogCategory.OPENEJB.createChild("tomcat"), "org.apache.openejb.util.resources");

  /** Associated standardServer. */
  private final StandardServer standardServer;

  /** Has the listener been started? */
  private boolean running = false;

  /** Associated naming resources. */
  private final NamingResources namingResources;

  public OpenEJBNamingContextListener(StandardServer standardServer) {
    this.standardServer = standardServer;
    namingResources = standardServer.getGlobalNamingResources();
  }

  public void lifecycleEvent(LifecycleEvent event) {
    if (event.getLifecycle() != standardServer) {
      return;
    }

    if (Lifecycle.START_EVENT.equals(event.getType())) {
      start();

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

      stop();
    }
  }

  public boolean isRunning() {
    return running;
  }

  public void start() {
    if (running) return;

    namingResources.addPropertyChangeListener(this);
    processInitialNamingResources();

    running = true;
  }

  public void stop() {
    if (!running) return;

    namingResources.removePropertyChangeListener(this);

    running = false;
  }

  public void propertyChange(PropertyChangeEvent event) {
    if (!running) return;

    Object source = event.getSource();
    if (source == namingResources) {
      processGlobalResourcesChange(
          event.getPropertyName(), event.getOldValue(), event.getNewValue());
    }
  }

  /**
   * Process a property change on the global naming resources, by making the corresponding addition
   * or removal to OpenEJB.
   *
   * @param name Property name of the change to be processed
   * @param oldValue The old value (or <code>null</code> if adding)
   * @param newValue The new value (or <code>null</code> if removing)
   */
  private void processGlobalResourcesChange(String name, Object oldValue, Object newValue) {

    // NOTE - It seems that the Context for global JNDI resources
    // is left in read-write mode, so we do not have to change it here

    if (name.equals("ejb")) {
      if (oldValue != null) {
        ContextEjb ejb = (ContextEjb) oldValue;
        if (ejb.getName() != null) {
          removeEjb(ejb.getName());
        }
      }
      if (newValue != null) {
        ContextEjb ejb = (ContextEjb) newValue;
        if (ejb.getName() != null) {
          addEjb(ejb);
        }
      }
    } else if (name.equals("environment")) {
      if (oldValue != null) {
        ContextEnvironment env = (ContextEnvironment) oldValue;
        if (env.getName() != null) {
          removeEnvironment(env.getName());
        }
      }
      if (newValue != null) {
        ContextEnvironment env = (ContextEnvironment) newValue;
        if (env.getName() != null) {
          addEnvironment(env);
        }
      }
    } else if (name.equals("localEjb")) {
      if (oldValue != null) {
        ContextLocalEjb ejb = (ContextLocalEjb) oldValue;
        if (ejb.getName() != null) {
          removeLocalEjb(ejb.getName());
        }
      }
      if (newValue != null) {
        ContextLocalEjb ejb = (ContextLocalEjb) newValue;
        if (ejb.getName() != null) {
          addLocalEjb(ejb);
        }
      }
    } else if (name.equals("resource")) {
      if (oldValue != null) {
        ContextResource resource = (ContextResource) oldValue;
        if (resource.getName() != null) {
          removeResource(resource.getName());
        }
      }
      if (newValue != null) {
        ContextResource resource = (ContextResource) newValue;
        if (resource.getName() != null) {
          addResource(resource);
        }
      }
    } else if (name.equals("resourceEnvRef")) {
      if (oldValue != null) {
        ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) oldValue;
        if (resourceEnvRef.getName() != null) {
          removeResourceEnvRef(resourceEnvRef.getName());
        }
      }
      if (newValue != null) {
        ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) newValue;
        if (resourceEnvRef.getName() != null) {
          addResourceEnvRef(resourceEnvRef);
        }
      }
    } else if (name.equals("resourceLink")) {
      if (oldValue != null) {
        ContextResourceLink rl = (ContextResourceLink) oldValue;
        if (rl.getName() != null) {
          removeResourceLink(rl.getName());
        }
      }
      if (newValue != null) {
        ContextResourceLink rl = (ContextResourceLink) newValue;
        if (rl.getName() != null) {
          addResourceLink(rl);
        }
      }
    }
  }

  private void processInitialNamingResources() {
    // Resource links
    ContextResourceLink[] resourceLinks = namingResources.findResourceLinks();
    for (ContextResourceLink resourceLink : resourceLinks) {
      addResourceLink(resourceLink);
    }

    // Resources
    ContextResource[] resources = namingResources.findResources();
    for (ContextResource resource : resources) {
      addResource(resource);
    }

    // Resources Env
    ContextResourceEnvRef[] resourceEnvRefs = namingResources.findResourceEnvRefs();
    for (ContextResourceEnvRef resourceEnvRef : resourceEnvRefs) {
      addResourceEnvRef(resourceEnvRef);
    }

    // Environment entries
    ContextEnvironment[] contextEnvironments = namingResources.findEnvironments();
    for (ContextEnvironment contextEnvironment : contextEnvironments) {
      addEnvironment(contextEnvironment);
    }

    // EJB references
    ContextEjb[] ejbs = namingResources.findEjbs();
    for (ContextEjb ejb : ejbs) {
      addEjb(ejb);
    }
  }

  public void addEjb(ContextEjb ejb) {}

  public void addEnvironment(ContextEnvironment env) {
    bindResource(env);
  }

  public void addLocalEjb(ContextLocalEjb localEjb) {}

  public void addResource(ContextResource resource) {
    bindResource(resource);
  }

  public void addResourceEnvRef(ContextResourceEnvRef resourceEnvRef) {
    bindResource(resourceEnvRef);
  }

  private void bindResource(ResourceBase res) {
    try {
      Context globalNamingContext = standardServer.getGlobalNamingContext();
      Object value = globalNamingContext.lookup(res.getName());
      String type = res.getType();
      bindResource(res.getName(), value, type);
    } catch (NamingException e) {
      logger.error("Unable to lookup Global Tomcat resource " + res.getName(), e);
    }
  }

  private void bindResource(final String name, final Object value, final String type) {
    Assembler assembler =
        (Assembler) SystemInstance.get().getComponent(org.apache.openejb.spi.Assembler.class);
    try {
      assembler
          .getContainerSystem()
          .getJNDIContext()
          .lookup(Assembler.OPENEJB_RESOURCE_JNDI_PREFIX + name);
      return;
    } catch (NamingException ne) {
      // no-op: OK
    }

    final ResourceInfo resourceInfo = new ResourceInfo();
    resourceInfo.id = name;
    resourceInfo.service = "Resource";
    resourceInfo.types.add(type);
    PassthroughFactory.add(resourceInfo, value);

    logger.info(
        "Importing a Tomcat Resource with id '" + resourceInfo.id + "' of type '" + type + "'.");
    try {
      assembler.createResource(resourceInfo);
    } catch (OpenEJBException e) {
      logger.error("Unable to bind Global Tomcat resource " + name + " into OpenEJB", e);
    }
  }

  public void addResourceLink(ContextResourceLink resourceLink) {}

  public void removeEjb(String name) {}

  public void removeEnvironment(String name) {}

  public void removeLocalEjb(String name) {}

  public void removeResource(String name) {
    // there isn't any way to remove a resource yet
  }

  public void removeResourceEnvRef(String name) {
    // there isn't any way to remove a resource yet
  }

  public void removeResourceLink(String name) {}
}
/** @version $Rev$ $Date$ */
public class BeginWebBeansListener
    implements ServletContextListener,
        ServletRequestListener,
        HttpSessionListener,
        HttpSessionActivationListener {

  private final String contextKey;

  /** Logger instance */
  private static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB_CDI, BeginWebBeansListener.class);

  protected FailOverService failoverService;

  /** Manages the container lifecycle */
  protected WebBeansContext webBeansContext;

  /**
   * Default constructor
   *
   * @param webBeansContext the OWB context
   */
  public BeginWebBeansListener(WebBeansContext webBeansContext) {
    this.webBeansContext = webBeansContext;
    this.failoverService = this.webBeansContext.getService(FailOverService.class);
    this.contextKey = "org.apache.tomee.catalina.WebBeansListener@" + webBeansContext.hashCode();
  }

  /** {@inheritDoc} */
  @Override
  public void requestDestroyed(ServletRequestEvent event) {
    // no-op
  }

  /** {@inheritDoc} */
  @Override
  public void requestInitialized(final ServletRequestEvent event) {
    final Object oldContext = ThreadSingletonServiceImpl.enter(this.webBeansContext);
    if (event != null) {
      event.getServletRequest().setAttribute(contextKey, oldContext);
    }

    try {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Starting a new request : [{0}]",
            event == null ? "null" : event.getServletRequest().getRemoteAddr());
      }

      if (webBeansContext instanceof WebappWebBeansContext) { // start before child
        ((WebappWebBeansContext) webBeansContext)
            .getParent()
            .getContextsService()
            .startContext(RequestScoped.class, event);
      }
      this.webBeansContext.getContextsService().startContext(RequestScoped.class, event);

      // we don't initialise the Session here but do it lazily if it gets requested
      // the first time. See OWB-457

    } catch (final Exception e) {
      logger.error(OWBLogConst.ERROR_0019, event == null ? "null" : event.getServletRequest());
      WebBeansUtil.throwRuntimeExceptions(e);
    }
  }

  /** {@inheritDoc} */
  @Override
  public void sessionCreated(final HttpSessionEvent event) {
    try {
      if (logger.isDebugEnabled()) {
        logger.debug("Starting a session with session id : [{0}]", event.getSession().getId());
      }
      if (webBeansContext instanceof WebappWebBeansContext) { // start before child
        ((WebappWebBeansContext) webBeansContext)
            .getParent()
            .getContextsService()
            .startContext(SessionScoped.class, event.getSession());
      }
      this.webBeansContext
          .getContextsService()
          .startContext(SessionScoped.class, event.getSession());
    } catch (final Exception e) {
      logger.error(OWBLogConst.ERROR_0020, event.getSession());
      WebBeansUtil.throwRuntimeExceptions(e);
    }
  }

  /** {@inheritDoc} */
  @Override
  public void sessionDestroyed(HttpSessionEvent event) {
    ensureRequestScope();
  }

  private void ensureRequestScope() {
    final Context reqCtx =
        webBeansContext.getContextsService().getCurrentContext(RequestScoped.class);
    if (reqCtx == null
        || !webBeansContext
            .getContextsService()
            .getCurrentContext(RequestScoped.class)
            .isActive()) {
      requestInitialized(null);
      EndWebBeansListener.FAKE_REQUEST.set(true);
    }
  }

  @Override
  public void sessionWillPassivate(HttpSessionEvent event) {
    ensureRequestScope();
  }

  @Override
  public void sessionDidActivate(HttpSessionEvent event) {
    if (failoverService.isSupportFailOver() || failoverService.isSupportPassivation()) {
      failoverService.sessionDidActivate(event.getSession());
    }
  }

  @Override
  public void contextInitialized(ServletContextEvent servletContextEvent) {
    try {
      OpenEJBLifecycle.initializeServletContext(
          servletContextEvent.getServletContext(), webBeansContext);
    } catch (final Exception e) {
      logger.warning(e.getMessage(), e);
    }
    ensureRequestScope();
  }

  @Override
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    ensureRequestScope();
  }
}
/** @version $Rev$ $Date$ */
public class OptimizedLoaderService implements LoaderService {

  private static final Logger log =
      Logger.getInstance(LogCategory.OPENEJB.createChild("cdi"), OptimizedLoaderService.class);

  public static final ThreadLocal<Collection<String>> ADDITIONAL_EXTENSIONS =
      new ThreadLocal<Collection<String>>();

  private final LoaderService loaderService;

  public OptimizedLoaderService() {
    this(new DefaultLoaderService());
  }

  public OptimizedLoaderService(final LoaderService loaderService) {
    this.loaderService = loaderService;
  }

  @Override
  public <T> List<T> load(final Class<T> serviceType) {
    return load(serviceType, Thread.currentThread().getContextClassLoader());
  }

  @Override
  public <T> List<T> load(final Class<T> serviceType, final ClassLoader classLoader) {
    // ServiceLoader is expensive (can take up to a half second).  This is an optimization
    if (OpenWebBeansPlugin.class.equals(serviceType)) {
      return (List<T>) loadWebBeansPlugins(classLoader);
    }

    // As far as we know, this only is reached for CDI Extension discovery
    if (Extension.class.equals(serviceType)) {
      return (List<T>) loadExtensions(classLoader);
    }
    return loaderService.load(serviceType, classLoader);
  }

  protected List<? extends Extension> loadExtensions(final ClassLoader classLoader) {
    final List<Extension> list = loaderService.load(Extension.class, classLoader);
    final Collection<String> additional = ADDITIONAL_EXTENSIONS.get();
    if (additional != null) {
      for (final String name : additional) {
        try {
          list.add(Extension.class.cast(classLoader.loadClass(name).newInstance()));
        } catch (final Exception ignored) {
          // no-op
        }
      }
    }

    final Collection<Extension> extensionCopy = new ArrayList<>(list);

    final Iterator<Extension> it = list.iterator();
    while (it.hasNext()) {
      if (it.hasNext()) {
        if (isFiltered(extensionCopy, it.next())) {
          it.remove();
        }
      }
    }
    return list;
  }

  // mainly intended to avoid conflicts between internal and overrided spec extensions
  private boolean isFiltered(final Collection<Extension> extensions, final Extension next) {
    final ClassLoader containerLoader = ParentClassLoaderFinder.Helper.get();
    final Class<? extends Extension> extClass = next.getClass();
    if (extClass.getClassLoader() != containerLoader) {
      return false;
    }

    final String name = extClass.getName();
    switch (name) {
      case "org.apache.bval.cdi.BValExtension":
        for (final Extension e : extensions) {
          final String en = e.getClass().getName();

          // org.hibernate.validator.internal.cdi.ValidationExtension but allowing few evolutions of
          // packages
          if (en.startsWith("org.hibernate.validator.") && en.endsWith("ValidationExtension")) {
            log.info("Skipping BVal CDI integration cause hibernate was found in the application");
            return true;
          }
        }
        break;
      case "org.apache.batchee.container.cdi.BatchCDIInjectionExtension": // see
                                                                          // org.apache.openejb.batchee.BatchEEServiceManager
        return "true"
            .equals(SystemInstance.get().getProperty("tomee.batchee.cdi.use-extension", "false"));
      case "org.apache.commons.jcs.jcache.cdi.MakeJCacheCDIInterceptorFriendly":
        final String spi = "META-INF/services/javax.cache.spi.CachingProvider";
        try {
          final Enumeration<URL> appResources =
              Thread.currentThread().getContextClassLoader().getResources(spi);
          if (appResources != null && appResources.hasMoreElements()) {
            final Collection<URL> containerResources =
                Collections.list(containerLoader.getResources(spi));
            do {
              if (!containerResources.contains(appResources.nextElement())) {
                log.info(
                    "Skipping JCS CDI integration cause another provide was found in the application");
                return true;
              }
            } while (appResources.hasMoreElements());
          }
        } catch (final Exception e) {
          // no-op
        }
        break;
      default:
    }
    return false;
  }

  private List<? extends OpenWebBeansPlugin> loadWebBeansPlugins(final ClassLoader loader) {
    final List<OpenWebBeansPlugin> list = new ArrayList<>(2);
    list.add(new CdiPlugin());
    {
      final Class<?> clazz;
      try {
        clazz = loader.loadClass("org.apache.geronimo.openejb.cdi.GeronimoWebBeansPlugin");
        try {
          list.add(OpenWebBeansPlugin.class.cast(clazz.newInstance()));
        } catch (final Exception e) {
          log.error("Unable to load OpenWebBeansPlugin: GeronimoWebBeansPlugin");
        }
      } catch (final ClassNotFoundException e) {
        // ignore
      }
    }
    {
      final Class<?> clazz;
      try {
        clazz = loader.loadClass("org.apache.webbeans.jsf.plugin.OpenWebBeansJsfPlugin");
        try {
          list.add(
              OpenWebBeansPlugin.class.cast(
                  Proxy.newProxyInstance(
                      loader,
                      new Class<?>[] {OpenWebBeansPlugin.class},
                      new ClassLoaderAwareHandler(
                          clazz.getSimpleName(), clazz.newInstance(), loader))));
        } catch (final Exception e) {
          log.error("Unable to load OpenWebBeansPlugin: OpenWebBeansJsfPlugin");
        }
      } catch (final ClassNotFoundException e) {
        // ignore
      }
    }
    return list;
  }
}
// TODO: rework it
// constraint: be able to use EM in web components (contextDestroyed() listener for instance) and
// opposite (link TWAB/Assembler)
// issue: StandardRoot is not lazy stopped
// proposals:
// - change the Assembler TWAB.undeployWebapps call to be correct.
// - lazy stop StandardRoot
// - integrate more finely with StandardContext to be able to ensure we are called when expected
public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
  private static final Logger LOGGER =
      Logger.getInstance(LogCategory.OPENEJB, TomEEWebappClassLoader.class.getName());
  private static final ThreadLocal<ClassLoaderConfigurer> INIT_CONFIGURER = new ThreadLocal<>();
  private static final ThreadLocal<Context> CONTEXT = new ThreadLocal<>();

  public static final String TOMEE_WEBAPP_FIRST = "tomee.webapp-first";
  public static final String TOMEE_EAR_DEFAULT = "tomee.ear.webapp-first";

  static {
    boolean result = ClassLoader.registerAsParallelCapable();
    if (!result) {
      LOGGER.warning("Can't register // tomee webapp classloader");
    }
  }

  public static final String CLASS_EXTENSION = ".class";

  private boolean restarting;
  private boolean forceStopPhase =
      Boolean.parseBoolean(
          SystemInstance.get().getProperty("tomee.webappclassloader.force-stop-phase", "false"));
  private ClassLoaderConfigurer configurer;
  private final boolean isEar;
  private final ClassLoader containerClassLoader;
  private volatile boolean originalDelegate;
  private final int hashCode;
  private Collection<File> additionalRepos;
  private volatile boolean stopped = false;
  private final Map<String, Boolean> filterTempCache =
      new HashMap<>(); // used only in sync block + isEar
  private volatile LazyStopStandardRoot webResourceRoot;

  public TomEEWebappClassLoader() {
    hashCode = construct();
    setJavaseClassLoader(getSystemClassLoader());
    containerClassLoader = ParentClassLoaderFinder.Helper.get();
    isEar =
        getParent() != null && !getParent().equals(containerClassLoader) && defaultEarBehavior();
    originalDelegate = getDelegate();
  }

  public TomEEWebappClassLoader(final ClassLoader parent) {
    super(parent);
    hashCode = construct();
    setJavaseClassLoader(getSystemClassLoader());
    containerClassLoader = ParentClassLoaderFinder.Helper.get();
    isEar =
        getParent() != null && !getParent().equals(containerClassLoader) && defaultEarBehavior();
    originalDelegate = getDelegate();
  }

  private int construct() {
    setDelegate(isDelegate());
    configurer = INIT_CONFIGURER.get();
    return super.hashCode();
  }

  @Override
  public void setDelegate(final boolean delegate) {
    this.delegate = delegate;
    this.originalDelegate = delegate;
  }

  @Override
  public void stop() throws LifecycleException {
    // in our destroyapplication method we need a valid classloader to
    // TomcatWebAppBuilder.afterStop()
    if (forceStopPhase || restarting) {
      internalStop();
    }
  }

  public Collection<File> getAdditionalRepos() {
    initAdditionalRepos();
    return additionalRepos;
  }

  @Override
  public Class<?> loadClass(final String name, final boolean resolve)
      throws ClassNotFoundException {
    if ("org.apache.openejb.hibernate.OpenEJBJtaPlatform".equals(name)
        || "org.apache.openejb.jpa.integration.hibernate.PrefixNamingStrategy".equals(name)
        || "org.apache.openejb.jpa.integration.eclipselink.PrefixSessionCustomizer".equals(name)
        || "org.apache.openejb.jpa.integration.eclipselink.OpenEJBServerPlatform".equals(name)
        || "org.apache.openejb.jpa.integration.eclipselink.OpenEJBServerPlatform$OpenEJBJTATransactionController"
            .equals(name)
        || "org.apache.openejb.eclipselink.JTATransactionController".equals(name)
        || "org.apache.tomee.mojarra.TomEEInjectionProvider".equals(name)) {
      // don't load them from system classloader (breaks all in embedded mode and no sense in other
      // cases)
      synchronized (this) {
        final ClassLoader old = getJavaseClassLoader();
        setJavaseClassLoader(NoClassClassLoader.INSTANCE);
        delegate = false;
        try {
          return super.loadClass(name, resolve);
        } finally {
          setJavaseClassLoader(old);
          setDelegate(originalDelegate);
        }
      }
    }

    // avoid to redefine classes from server in this classloader is it not already loaded
    if (URLClassLoaderFirst.shouldDelegateToTheContainer(
        this, name)) { // dynamic validation handling overriding
      try {
        return OpenEJB.class
            .getClassLoader()
            .loadClass(
                name); // we could use containerClassLoader but this is server loader so cut it even
                       // more
      } catch (final ClassNotFoundException e) {
        synchronized (this) {
          return super.loadClass(name, resolve);
        }
      } catch (final NoClassDefFoundError ncdfe) {
        synchronized (this) {
          return super.loadClass(name, resolve);
        }
      }
    } else if (name.startsWith("javax.faces.") || name.startsWith("org.apache.webbeans.jsf")) {
      synchronized (this) {
        delegate = false;
        try {
          return super.loadClass(name, resolve);
        } finally {
          setDelegate(originalDelegate);
        }
      }
    }
    synchronized (
        this) { // TODO: rework it to avoid it and get aligned on Java 7 classloaders (but not a big
                // issue)
      if (isEar) {
        final boolean filter = filter(name, true);
        filterTempCache.put(name, filter); // will be called again by super.loadClass() so cache it
        if (!filter) {
          if (URLClassLoaderFirst.class.isInstance(getParent())) { // true
            final URLClassLoaderFirst urlClassLoaderFirst =
                URLClassLoaderFirst.class.cast(getParent());
            Class<?> c = urlClassLoaderFirst.findAlreadyLoadedClass(name);
            if (c != null) {
              return c;
            }
            c = urlClassLoaderFirst.loadInternal(name, resolve);
            if (c != null) {
              return c;
            }
          }
          return loadWithDelegate(
              getResource(name.replace('.', '/') + CLASS_EXTENSION) == null, resolve, name);
        }
      }
      return super.loadClass(name, resolve);
    }
  }

  private Class<?> loadWithDelegate(
      final boolean delegate, final boolean resolve, final String name)
      throws ClassNotFoundException {
    setDelegate(delegate);
    try {
      return super.loadClass(name, resolve);
    } finally {
      filterTempCache.remove(name); // no more needed since class is loaded, avoid to waste mem
      setDelegate(originalDelegate);
    }
  }

  @Override
  public void setResources(final WebResourceRoot resources) {
    this.resources = resources;
    if (StandardRoot.class.isInstance(resources)) {
      final List<WebResourceSet> jars =
          (List<WebResourceSet>) Reflections.get(resources, "jarResources");
      if (jars != null && !jars.isEmpty()) {
        final Iterator<WebResourceSet> jarIt = jars.iterator();
        while (jarIt.hasNext()) {
          final WebResourceSet set = jarIt.next();
          if (set.getBaseUrl() == null) {
            continue;
          }
          final File file = URLs.toFile(set.getBaseUrl());
          try {
            if (file.exists()
                && (!TomEEClassLoaderEnricher.validateJarFile(file) || !jarIsAccepted(file))) {
              // need to remove this resource
              LOGGER.warning("Removing " + file.getAbsolutePath() + " since it is offending");
              jarIt.remove();
            }
          } catch (final IOException e) {
            // ignore
          }
        }
      }
    }
  }

  @Override
  protected boolean filter(final String inName, final boolean isClassName) {
    final String name =
        inName == null || isClassName ? inName : inName.replace('/', '.').replace(".class", "");
    if ("org.apache.tomee.mojarra.TomEEInjectionProvider".equals(name)) {
      return false;
    }
    if (isEar) { // check we are called from super and we already cached the result in loadClass
      synchronized (this) {
        final Boolean cache = filterTempCache.get(name);
        if (cache != null) {
          return cache;
        }
      }
    }
    return URLClassLoaderFirst.shouldSkip(name);
  }

  public void internalDestroy() {
    try {
      if (!stopped) {
        try {
          internalStop();
        } catch (final LifecycleException e) {
          // no-op
        }
      }
      super.destroy();
    } finally {
      cleanUpClassLoader();
    }
  }

  public void internalStop() throws LifecycleException {
    if (stopped) {
      return;
    }
    // reset classloader because of tomcat classloaderlogmanager
    // to be sure we reset the right loggers
    final Thread thread = Thread.currentThread();
    final ClassLoader loader = thread.getContextClassLoader();
    thread.setContextClassLoader(this);
    try {
      super.stop();
      // super.destroy();
      if (webResourceRoot != null) {
        webResourceRoot.internalStop();
        webResourceRoot = null;
      }
      stopped = true;
    } finally {
      thread.setContextClassLoader(loader);
      if (!forceStopPhase) {
        cleanUpClassLoader();
      }
    }
  }

  public void restarting() {
    restarting = true;
  }

  public void restarted() {
    restarting = false;
  }

  public boolean isRestarting() {
    return restarting;
  }

  public boolean isForceStopPhase() {
    return forceStopPhase;
  }

  public boolean isStopped() {
    return stopped;
  }

  public synchronized void initAdditionalRepos() {
    if (additionalRepos != null) {
      return;
    }
    if (CONTEXT.get() != null) {
      additionalRepos = new LinkedList<>();
      final String contextPath = CONTEXT.get().getServletContext().getContextPath();
      final String name = contextPath.isEmpty() ? "ROOT" : contextPath.substring(1);
      final String externalRepositories =
          SystemInstance.get().getProperty("tomee." + name + ".externalRepositories");
      if (externalRepositories != null) {
        for (final String additional : externalRepositories.split(",")) {
          final String trim = additional.trim();
          if (!trim.isEmpty()) {
            final File file = new File(trim);
            additionalRepos.add(file);
          }
        }
      }
    }
  }

  // embeddeding implementation of sthg (JPA, JSF) can lead to classloading issues if we don't
  // enrich the webapp
  // with our integration jars
  // typically the class will try to be loaded by the common classloader
  // but the interface implemented or the parent class
  // will be in the webapp
  @Override
  public void start() throws LifecycleException {
    super.start(); // do it first otherwise we can't use this as classloader

    // mainly for tomee-maven-plugin
    initAdditionalRepos();
    if (additionalRepos != null && !additionalRepos.isEmpty()) {
      for (final File f : additionalRepos) {
        final DirResourceSet webResourceSet =
            new PremptiveDirResourceSet(resources, "/", f.getAbsolutePath(), "/");
        resources.addPreResources(webResourceSet);
      }
      resources.setCachingAllowed(false);
    }

    // add configurer enrichments
    if (configurer != null) {
      // add now we removed all we wanted
      final URL[] enrichment = configurer.additionalURLs();
      for (final URL url : enrichment) {
        super.addURL(url);
      }
    }

    // add internal enrichments
    for (final URL url : SystemInstance.get().getComponent(WebAppEnricher.class).enrichment(this)) {
      super.addURL(url);
    }

    // WEB-INF/jars.xml
    final File war = Contexts.warPath(CONTEXT.get());
    final File jarsXml = new File(war, "WEB-INF/" + QuickJarsTxtParser.FILE_NAME);
    final ClassLoaderConfigurer configurerTxt = QuickJarsTxtParser.parse(jarsXml);
    if (configurerTxt != null) {
      configurer = new CompositeClassLoaderConfigurer(configurer, configurerTxt);
    }

    stopped = false;
  }

  public void addURL(final URL url) {
    if (configurer == null || configurer.accept(url)) {
      super.addURL(url);
    }
  }

  private boolean jarIsAccepted(final File file) {
    if (configurer == null) {
      return true;
    }

    try {
      if (!configurer.accept(file.toURI().toURL())) {
        LOGGER.warning(
            "jar '"
                + file.getAbsolutePath()
                + "' is excluded: "
                + file.getName()
                + ". It will be ignored.");
        return false;
      }
    } catch (final MalformedURLException e) {
      // no-op
    }
    return true;
  }

  protected boolean defaultEarBehavior() {
    return !SystemInstance.get().getOptions().get(TOMEE_EAR_DEFAULT, false /*bck compat*/);
  }

  private static boolean isDelegate() {
    return !SystemInstance.get().getOptions().get(TOMEE_WEBAPP_FIRST, true);
  }

  @Override
  public InputStream getResourceAsStream(final String name) {
    if (!getState().isAvailable()) {
      return null;
    }
    try {
      return super.getResourceAsStream(name);
    } catch (final NullPointerException npe) {
      // workaround cause of a bug in tomcat 8.5.0, keeping it even if we upgraded until we don't
      // support 8.5.0 anymore
      final URL url = super.getResource(name);
      if (url != null) {
        try {
          return url.openStream();
        } catch (final IOException e) {
          // no-op
        }
      }
      return null;
    }
  }

  @Override
  public Enumeration<URL> getResources(final String name) throws IOException {
    if (!getState().isAvailable()) {
      return null;
    }

    if ("META-INF/services/javax.servlet.ServletContainerInitializer".equals(name)) {
      final Collection<URL> list = new ArrayList<>(Collections.list(super.getResources(name)));
      final Iterator<URL> it = list.iterator();
      while (it.hasNext()) {
        final URL next = it.next();
        final File file = Files.toFile(next);
        if (!file.isFile() && NewLoaderLogic.skip(next)) {
          it.remove();
        }
      }
      return Collections.enumeration(list);
    }
    if ("META-INF/services/javax.websocket.ContainerProvider".equals(name)) {
      final Collection<URL> list = new ArrayList<>(Collections.list(super.getResources(name)));
      final Iterator<URL> it = list.iterator();
      while (it.hasNext()) {
        final URL next = it.next();
        final File file = Files.toFile(next);
        if (!file.isFile() && NewLoaderLogic.skip(next)) {
          it.remove();
        }
      }
      return Collections.enumeration(list);
    }
    if ("META-INF/faces-config.xml".equals(name)) { // mojarra workaround
      try {
        if (WebBeansContext.currentInstance() == null
            && Boolean.parseBoolean(
                SystemInstance.get().getProperty("tomee.jsf.ignore-owb", "true"))) {
          final Collection<URL> list = new HashSet<>(Collections.list(super.getResources(name)));
          final Iterator<URL> it = list.iterator();
          while (it.hasNext()) {
            final String fileName = Files.toFile(it.next()).getName();
            if (fileName.startsWith("openwebbeans-" /*jsf|el22*/) && fileName.endsWith(".jar")) {
              it.remove();
            }
          }
          return Collections.enumeration(list);
        }
      } catch (final Throwable th) {
        // no-op
      }
    }
    return URLClassLoaderFirst.filterResources(name, super.getResources(name));
  }

  @Override
  public boolean equals(final Object other) {
    return other != null && ClassLoader.class.isInstance(other) && hashCode() == other.hashCode();
  }

  @Override
  public int hashCode() {
    return hashCode;
  }

  @Override
  public TomEEWebappClassLoader copyWithoutTransformers() {
    final TomEEWebappClassLoader result = new TomEEWebappClassLoader(getParent());
    result.additionalRepos = additionalRepos;
    result.configurer = configurer;
    super.copyStateWithoutTransformers(result);
    try {
      result.start();
    } catch (LifecycleException e) {
      throw new IllegalStateException(e);
    }

    return result;
  }

  @Override
  public void destroy() {
    if (forceStopPhase) {
      internalDestroy();
    }
  }

  private void cleanUpClassLoader() {
    final LogManager lm = LogManager.getLogManager();
    if (ClassLoaderLogManager.class.isInstance(
        lm)) { // weak ref but ensure it is really removed otherwise in some cases we leak
      Map.class.cast(Reflections.get(lm, "classLoaderLoggers")).remove(this);
    }
  }

  public static void initContext(final ClassLoaderConfigurer configurer) {
    INIT_CONFIGURER.set(configurer);
  }

  public static void initContext(final Context ctx) {
    CONTEXT.set(ctx);
  }

  public static void cleanContext() {
    INIT_CONFIGURER.remove();
    CONTEXT.remove();
  }

  public void setWebResourceRoot(LazyStopStandardRoot webResourceRoot) {
    this.webResourceRoot = webResourceRoot;
  }

  private static class NoClassClassLoader extends ClassLoader {
    private static final NoClassClassLoader INSTANCE = new NoClassClassLoader();

    @Override
    public Class<?> loadClass(final String name) throws ClassNotFoundException {
      throw new ClassNotFoundException();
    }
  }

  private static final class PremptiveDirResourceSet extends DirResourceSet {
    private static final String WEB_INF_CLASSES = "/WEB-INF/classes";

    public PremptiveDirResourceSet(
        final WebResourceRoot resources,
        final String s,
        final String absolutePath,
        final String s1) {
      super(resources, s, absolutePath, s1);
    }

    @Override
    public WebResource getResource(final String path) {
      return super.getResource(computePath(path));
    }

    @Override
    public String[] list(final String path) {
      return super.list(computePath(path));
    }

    private static String computePath(final String path) {
      if (WEB_INF_CLASSES.equals(path)) {
        return "/";
      }
      return path.startsWith(WEB_INF_CLASSES) ? path.substring(WEB_INF_CLASSES.length()) : path;
    }
  }
}
public class SingletonInstanceManager {
  private static final Logger logger =
      Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");

  private SecurityService securityService;
  private final SingletonContext sessionContext;
  private final WebServiceContext webServiceContext;

  public SingletonInstanceManager(SecurityService securityService) {
    this.securityService = securityService;
    sessionContext = new SingletonContext(this.securityService);
    webServiceContext = new EjbWsContext(sessionContext);
  }

  protected void start(BeanContext beanContext) throws OpenEJBException {
    if (beanContext.isLoadOnStartup()) {
      initialize(beanContext);
    }
  }

  private void initialize(BeanContext beanContext) throws OpenEJBException {
    try {
      ThreadContext callContext = new ThreadContext(beanContext, null);
      ThreadContext old = ThreadContext.enter(callContext);
      try {
        getInstance(callContext);
      } finally {
        ThreadContext.exit(old);
      }
    } catch (OpenEJBException e) {
      throw new OpenEJBException("Singleton startup failed: " + beanContext.getDeploymentID(), e);
    }
  }

  public Instance getInstance(final ThreadContext callContext) throws OpenEJBException {
    final BeanContext beanContext = callContext.getBeanContext();
    Data data = (Data) beanContext.getContainerData();
    AtomicReference<Future<Instance>> singleton = data.singleton;
    try {
      // Has the singleton been created yet?
      // If there is a Future object in the AtomicReference, then
      // it's either been created or is being created now.
      Future<Instance> singletonFuture = singleton.get();
      if (singletonFuture != null) return singletonFuture.get();

      // The singleton has not been created nor is being created
      // We will construct this FutureTask and compete with the
      // other threads for the right to create the singleton
      FutureTask<Instance> task =
          new FutureTask<Instance>(
              new Callable<Instance>() {
                public Instance call() throws Exception {
                  return createInstance(callContext, beanContext);
                }
              });

      do {
        // If our FutureTask was the one to win the slot
        // than we are the ones responsible for creating
        // the singleton while the others wait.
        if (singleton.compareAndSet(null, task)) {
          task.run();
        }

        // If we didn't win the slot and no other FutureTask
        // has been set by a different thread, than we need
        // to try again.
      } while ((singletonFuture = singleton.get()) == null);

      // At this point we can safely return the singleton
      return singletonFuture.get();

    } catch (InterruptedException e) {
      Thread.interrupted();
      throw new ApplicationException(
          new NoSuchEJBException("Singleton initialization interrupted").initCause(e));
    } catch (ExecutionException e) {
      Throwable throwable = e.getCause();
      if (throwable instanceof ApplicationException) {
        throw (ApplicationException) throwable;
      }

      throw new ApplicationException(
          new NoSuchEJBException("Singleton initialization failed").initCause(e.getCause()));
    }
  }

  private void initializeDependencies(BeanContext beanContext) throws OpenEJBException {
    SystemInstance systemInstance = SystemInstance.get();
    ContainerSystem containerSystem = systemInstance.getComponent(ContainerSystem.class);
    for (String dependencyId : beanContext.getDependsOn()) {
      BeanContext dependencyContext = containerSystem.getBeanContext(dependencyId);
      if (dependencyContext == null) {
        throw new OpenEJBException(
            "Deployment does not exist. Deployment(id='" + dependencyContext + "')");
      }

      final Object containerData = dependencyContext.getContainerData();

      // Bean may not be a singleton or may be a singleton
      // managed by a different container implementation
      if (containerData instanceof Data) {
        Data data = (Data) containerData;

        data.initialize();
      }
    }
  }

  private Instance createInstance(ThreadContext callContext, BeanContext beanContext)
      throws ApplicationException {
    try {
      initializeDependencies(beanContext);

      final InstanceContext context = beanContext.newInstance();

      if (context.getBean() instanceof SessionBean) {

        final Operation originalOperation = callContext.getCurrentOperation();
        try {
          callContext.setCurrentOperation(Operation.CREATE);
          final Method create = beanContext.getCreateMethod();
          final InterceptorStack ejbCreate =
              new InterceptorStack(
                  context.getBean(),
                  create,
                  Operation.CREATE,
                  new ArrayList<InterceptorData>(),
                  new HashMap());
          ejbCreate.invoke();
        } finally {
          callContext.setCurrentOperation(originalOperation);
        }
      }

      ReadWriteLock lock;
      if (beanContext.isBeanManagedConcurrency()) {
        // Bean-Managed Concurrency
        lock = new BeanManagedLock();
      } else {
        // Container-Managed Concurrency
        lock = new ReentrantReadWriteLock();
      }

      return new Instance(
          context.getBean(), context.getInterceptors(), context.getCreationalContext(), lock);
    } catch (Throwable e) {
      if (e instanceof java.lang.reflect.InvocationTargetException) {
        e = ((java.lang.reflect.InvocationTargetException) e).getTargetException();
      }
      String t =
          "The bean instance " + beanContext.getDeploymentID() + " threw a system exception:" + e;
      logger.error(t, e);
      throw new ApplicationException(
          new NoSuchEJBException("Singleton failed to initialize").initCause(e));
    }
  }

  public void freeInstance(ThreadContext callContext) {
    BeanContext beanContext = callContext.getBeanContext();
    Data data = (Data) beanContext.getContainerData();
    Future<Instance> instanceFuture = data.singleton.get();

    // Possible the instance was never created
    if (instanceFuture == null) return;

    Instance instance;
    try {
      instance = instanceFuture.get();
    } catch (InterruptedException e) {
      Thread.interrupted();
      logger.error(
          "Singleton shutdown failed because the thread was interrupted: "
              + beanContext.getDeploymentID(),
          e);
      return;
    } catch (ExecutionException e) {
      // Instance was never initialized
      return;
    }

    try {
      callContext.setCurrentOperation(Operation.PRE_DESTROY);
      callContext.setCurrentAllowedStates(null);

      Method remove = instance.bean instanceof SessionBean ? beanContext.getCreateMethod() : null;

      List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
      InterceptorStack interceptorStack =
          new InterceptorStack(
              instance.bean,
              remove,
              Operation.PRE_DESTROY,
              callbackInterceptors,
              instance.interceptors);

      // Transaction Demarcation for Singleton PostConstruct method
      TransactionType transactionType;

      if (beanContext.getComponentType() == BeanType.SINGLETON) {
        Set<Method> callbacks =
            callbackInterceptors.get(callbackInterceptors.size() - 1).getPreDestroy();
        if (callbacks.isEmpty()) {
          transactionType = TransactionType.RequiresNew;
        } else {
          transactionType = beanContext.getTransactionType(callbacks.iterator().next());
          if (transactionType == TransactionType.Required) {
            transactionType = TransactionType.RequiresNew;
          }
        }
      } else {
        transactionType =
            beanContext.isBeanManagedTransaction()
                ? TransactionType.BeanManaged
                : TransactionType.NotSupported;
      }
      TransactionPolicy transactionPolicy =
          EjbTransactionUtil.createTransactionPolicy(transactionType, callContext);
      try {
        // Call the chain
        interceptorStack.invoke();
        if (instance.creationalContext != null) {
          instance.creationalContext.release();
        }
      } catch (Throwable e) {
        // RollBack Transaction
        EjbTransactionUtil.handleSystemException(transactionPolicy, e, callContext);
      } finally {
        EjbTransactionUtil.afterInvoke(transactionPolicy, callContext);
      }

    } catch (Throwable re) {
      logger.error("Singleton shutdown failed: " + beanContext.getDeploymentID(), re);
    }
  }

  /**
   * This method has no work to do as all instances are removed from the pool on getInstance(...)
   * and not returned via poolInstance(...) if they threw a system exception.
   *
   * @param callContext
   * @param bean
   */
  public void discardInstance(ThreadContext callContext, Object bean) {}

  public void deploy(BeanContext beanContext) throws OpenEJBException {
    Data data = new Data(beanContext);
    beanContext.setContainerData(data);

    beanContext.set(EJBContext.class, this.sessionContext);

    // Create stats interceptor
    StatsInterceptor stats = new StatsInterceptor(beanContext.getBeanClass());
    beanContext.addSystemInterceptor(stats);

    MBeanServer server = LocalMBeanServer.get();

    ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb.management");
    jmxName.set("J2EEServer", "openejb");
    jmxName.set("J2EEApplication", null);
    jmxName.set("EJBModule", beanContext.getModuleID());
    jmxName.set("SingletonSessionBean", beanContext.getEjbName());
    jmxName.set("j2eeType", "");
    jmxName.set("name", beanContext.getEjbName());

    // register the invocation stats interceptor
    try {
      ObjectName objectName = jmxName.set("j2eeType", "Invocations").build();
      server.registerMBean(new ManagedMBean(stats), objectName);
      data.add(objectName);
    } catch (Exception e) {
      logger.error("Unable to register MBean ", e);
    }

    try {
      final Context context = beanContext.getJndiEnc();
      context.bind("comp/EJBContext", sessionContext);
      context.bind("comp/WebServiceContext", webServiceContext);
      context.bind("comp/TimerService", new TimerServiceWrapper());
    } catch (NamingException e) {
      throw new OpenEJBException("Failed to bind EJBContext/WebServiceContext/TimerService", e);
    }
  }

  public void undeploy(BeanContext beanContext) {
    Data data = (Data) beanContext.getContainerData();
    if (data == null) return;

    MBeanServer server = LocalMBeanServer.get();
    for (ObjectName objectName : data.jmxNames) {
      try {
        server.unregisterMBean(objectName);
      } catch (Exception e) {
        logger.error("Unable to unregister MBean " + objectName);
      }
    }

    beanContext.setContainerData(null);
  }

  private final class Data {
    private final AtomicReference<Future<Instance>> singleton =
        new AtomicReference<Future<Instance>>();
    private final List<ObjectName> jmxNames = new ArrayList<ObjectName>();
    private final BeanContext info;

    public Data(BeanContext info) {
      this.info = info;
    }

    public ObjectName add(ObjectName name) {
      jmxNames.add(name);
      return name;
    }

    public void initialize() throws OpenEJBException {
      SingletonInstanceManager.this.initialize(info);
    }
  }

  private static class BeanManagedLock implements ReadWriteLock {
    private final Lock lock =
        new Lock() {
          public void lock() {}

          public void lockInterruptibly() {}

          public Condition newCondition() {
            throw new java.lang.UnsupportedOperationException("newCondition()");
          }

          public boolean tryLock() {
            return true;
          }

          public boolean tryLock(long time, TimeUnit unit) {
            return true;
          }

          public void unlock() {}
        };

    public Lock readLock() {
      return lock;
    }

    public Lock writeLock() {
      return lock;
    }
  }
}