/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomee.catalina.valve; import org.apache.catalina.Contained; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; import org.apache.catalina.Valve; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.ha.CatalinaCluster; import org.apache.catalina.ha.ClusterValve; import org.apache.openejb.config.sys.PropertiesAdapter; import org.apache.openejb.core.ParentClassLoaderFinder; import org.apache.tomee.catalina.TomEERuntimeException; import org.apache.xbean.recipe.ObjectRecipe; import org.apache.xbean.recipe.Option; import javax.servlet.ServletException; import java.io.IOException; import java.util.List; import java.util.Properties; import java.util.concurrent.CopyOnWriteArrayList; public class LazyValve implements ClusterValve, Lifecycle, Contained { private String delegateClassName; private String properties; private Container container; private Valve next; private CatalinaCluster cluster; private volatile Valve delegate; private volatile boolean init; private volatile boolean start; private volatile LifecycleState state; private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>(); public void setDelegateClassName(String delegateClassName) { this.delegateClassName = delegateClassName; } public void setProperties(final String properties) { this.properties = properties; } private Valve instance() { if (delegate == null) { synchronized (this) { if (delegate == null) { final Object instance; ClassLoader cl = loader(); if (cl == null) { return null; } final Class<?> clazz; try { clazz = cl.loadClass(delegateClassName); } catch (final ClassNotFoundException e) { throw new TomEERuntimeException(e); } try { final ObjectRecipe recipe = new ObjectRecipe(clazz); recipe.allow(Option.CASE_INSENSITIVE_PROPERTIES); recipe.allow(Option.IGNORE_MISSING_PROPERTIES); recipe.allow(Option.FIELD_INJECTION); recipe.allow(Option.PRIVATE_PROPERTIES); if (properties != null) { final Properties props = new PropertiesAdapter() .unmarshal(properties.trim().replaceAll("\\p{Space}*(\\p{Alnum}*)=", "\n$1=")); recipe.setAllProperties(props); } instance = recipe.create(); } catch (final Exception e) { throw new TomEERuntimeException(e); } delegate = Valve.class.cast(instance); delegate.setNext(next); if (Contained.class.isInstance(delegate)) { Contained.class.cast(delegate).setContainer(container); } if (Lifecycle.class.isInstance(delegate)) { if (init) { try { final Lifecycle lifecycle = Lifecycle.class.cast(delegate); for (final LifecycleListener listener : lifecycleListeners) { lifecycle.addLifecycleListener(listener); } lifecycle.init(); if (start) { lifecycle.start(); } } catch (final LifecycleException e) { // no-op } } } if (ClusterValve.class.isInstance(delegate)) { ClusterValve.class.cast(delegate).setCluster(cluster); } } } } return delegate; } private ClassLoader loader() { if (container == null || !Context.class.isInstance(container)) { return ParentClassLoaderFinder.Helper.get(); } final Context ctx = Context.class.cast(container); return ctx.getLoader() != null && ctx.getLoader().getClassLoader() != null ? ctx.getLoader().getClassLoader() : null; } @Override public Valve getNext() { return next; } @Override public void setNext(final Valve valve) { this.next = valve; if (delegate != null) { delegate.setNext(next); } } @Override public void backgroundProcess() { if (delegate != null) { delegate.backgroundProcess(); } } @Override public void invoke(final Request request, final Response response) throws IOException, ServletException { instance().invoke(request, response); } @Override public boolean isAsyncSupported() { return instance().isAsyncSupported(); } @Override public void addLifecycleListener(final LifecycleListener listener) { lifecycleListeners.add(listener); } @Override public LifecycleListener[] findLifecycleListeners() { return lifecycleListeners.toArray(new LifecycleListener[0]); } @Override public void removeLifecycleListener(final LifecycleListener listener) { lifecycleListeners.remove(listener); } @Override public void init() throws LifecycleException { if (instance() != null && Lifecycle.class.isInstance(delegate)) { Lifecycle.class.cast(delegate).init(); } else { init = true; } state = LifecycleState.INITIALIZED; } @Override public void start() throws LifecycleException { if (instance() != null && Lifecycle.class.isInstance(delegate)) { Lifecycle.class.cast(delegate).start(); } else { start = true; } state = LifecycleState.STARTED; } @Override public void stop() throws LifecycleException { if (instance() != null && Lifecycle.class.isInstance(delegate)) { Lifecycle.class.cast(delegate).stop(); } state = LifecycleState.STOPPED; } @Override public void destroy() throws LifecycleException { if (instance() != null && Lifecycle.class.isInstance(delegate)) { Lifecycle.class.cast(delegate).destroy(); } state = LifecycleState.DESTROYED; } @Override public LifecycleState getState() { return state; } @Override public String getStateName() { return state.name(); } @Override public Container getContainer() { return container; } @Override public void setContainer(final Container container) { this.container = container; if (delegate != null && Contained.class.isInstance(delegate)) { Contained.class.cast(delegate).setContainer(container); } } @Override public CatalinaCluster getCluster() { return cluster; } @Override public void setCluster(final CatalinaCluster catalinaCluster) { this.cluster = catalinaCluster; } }