All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.as.ee.component.ViewService Maven / Gradle / Ivy

There is a newer version: 35.0.0.Beta1
Show newest version
/*
 * Copyright The WildFly Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.jboss.as.ee.component;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jboss.as.ee.logging.EeLogger;
import org.jboss.as.ee.utils.DescriptorUtils;
import org.jboss.as.naming.ManagedReference;
import org.jboss.invocation.Interceptor;
import org.jboss.invocation.InterceptorContext;
import org.jboss.invocation.InterceptorFactory;
import org.jboss.invocation.Interceptors;
import org.jboss.invocation.SimpleInterceptorFactoryContext;
import org.jboss.invocation.proxy.ProxyFactory;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.manager.WildFlySecurityManager;

import static org.jboss.as.ee.logging.EeLogger.ROOT_LOGGER;

/**
 * @author David M. Lloyd
 */
public final class ViewService implements Service {

    private final InjectedValue componentInjector = new InjectedValue();
    private final Map viewInterceptorFactories;
    private final Map clientInterceptorFactories;
    private final InterceptorFactory clientPostConstruct;
    private final InterceptorFactory clientPreDestroy;
    private final ProxyFactory proxyFactory;
    private final Class viewClass;
    private final Set asyncMethods;
    private final ViewInstanceFactory viewInstanceFactory;
    private final Map, Object> privateData;
    private volatile ComponentView view;

    private volatile Interceptor clientPostConstructInterceptor;
    private volatile Interceptor clientPreDestroyInterceptor;
    private volatile Map clientInterceptors;


    public ViewService(final ViewConfiguration viewConfiguration) {
        viewClass = viewConfiguration.getViewClass();
        final ProxyFactory proxyFactory = viewConfiguration.getProxyFactory();
        this.proxyFactory = proxyFactory;
        final List methods = proxyFactory.getCachedMethods();
        final int methodCount = methods.size();
        clientPostConstruct = Interceptors.getChainedInterceptorFactory(viewConfiguration.getClientPostConstructInterceptors());
        clientPreDestroy = Interceptors.getChainedInterceptorFactory(viewConfiguration.getClientPreDestroyInterceptors());
        final IdentityHashMap viewInterceptorFactories = new IdentityHashMap(methodCount);
        final IdentityHashMap clientInterceptorFactories = new IdentityHashMap(methodCount);
        for (final Method method : methods) {
            if (method.getName().equals("finalize") && method.getParameterCount() == 0) {
                viewInterceptorFactories.put(method, Interceptors.getTerminalInterceptorFactory());
            } else {
                viewInterceptorFactories.put(method, Interceptors.getChainedInterceptorFactory(viewConfiguration.getViewInterceptors(method)));
                clientInterceptorFactories.put(method, Interceptors.getChainedInterceptorFactory(viewConfiguration.getClientInterceptors(method)));
            }
        }
        this.viewInterceptorFactories = viewInterceptorFactories;
        this.clientInterceptorFactories = clientInterceptorFactories;
        this.asyncMethods = viewConfiguration.getAsyncMethods();
        if (viewConfiguration.getViewInstanceFactory() == null) {
            viewInstanceFactory = new DefaultViewInstanceFactory();
        } else {
            viewInstanceFactory = viewConfiguration.getViewInstanceFactory();
        }
        if(viewConfiguration.getPrivateData().isEmpty()) {
            privateData = Collections.emptyMap();
        } else {
            privateData = viewConfiguration.getPrivateData();
        }
    }

    public void start(final StartContext context) throws StartException {
        // Construct the view
        View view = new View(privateData);
        view.initializeInterceptors();
        this.view = view;

        final SimpleInterceptorFactoryContext factoryContext = new SimpleInterceptorFactoryContext();
        final Component component = view.getComponent();
        factoryContext.getContextData().put(Component.class, component);
        factoryContext.getContextData().put(ComponentView.class, view);

        clientPostConstructInterceptor = clientPostConstruct.create(factoryContext);
        clientPreDestroyInterceptor = clientPreDestroy.create(factoryContext);

        final Map clientInterceptorFactories = ViewService.this.clientInterceptorFactories;
        clientInterceptors = new IdentityHashMap(clientInterceptorFactories.size());
        for (Map.Entry entry : clientInterceptorFactories.entrySet()) {
            clientInterceptors.put(entry.getKey(), entry.getValue().create(factoryContext));
        }


    }

    public void stop(final StopContext context) {
        view = null;
    }

    public Injector getComponentInjector() {
        return componentInjector;
    }

    public ComponentView getValue() throws IllegalStateException, IllegalArgumentException {
        return view;
    }

    class View implements ComponentView {

        private final Component component;
        private final Map viewInterceptors;
        private final Map methods;
        private final Map, Object> privateData;

        View(final Map, Object> privateData) {
            this.privateData = privateData;
            component = componentInjector.getValue();
            //we need to build the view interceptor chain
            this.viewInterceptors = new IdentityHashMap();
            this.methods = new HashMap();
        }

        void initializeInterceptors() {
            final SimpleInterceptorFactoryContext factoryContext = new SimpleInterceptorFactoryContext();
            final Map viewInterceptorFactories = ViewService.this.viewInterceptorFactories;
            factoryContext.getContextData().put(Component.class, component);
            //we don't have this code in the constructor so we avoid passing around
            //a half constructed instance
            factoryContext.getContextData().put(ComponentView.class, this);

            for (Map.Entry entry : viewInterceptorFactories.entrySet()) {
                Method method = entry.getKey();
                viewInterceptors.put(method, entry.getValue().create(factoryContext));
                methods.put(new MethodDescription(method.getName(), DescriptorUtils.methodDescriptor(method)), method);
            }

        }

        public ManagedReference createInstance() throws Exception {
            return createInstance(Collections.emptyMap());
        }

        public ManagedReference createInstance(Map contextData) throws Exception {
            // view instance creation can lead to instantiating application component classes (like the MDB implementation class
            // or even the Jakarta Enterprise Beans implementation class of a no-interface view exposing bean). Such class initialization needs to
            // have the TCCL set to the component/application's classloader. @see https://issues.jboss.org/browse/WFLY-3989
            final ClassLoader oldTCCL = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
            try {
                WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(component.getComponentClass());
                return viewInstanceFactory.createViewInstance(this, contextData);
            } finally {
                // reset the TCCL
                WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(oldTCCL);
            }
        }

        @Override
        public Object invoke(InterceptorContext interceptorContext) throws Exception {
            if(component instanceof BasicComponent) {
                ((BasicComponent) component).waitForComponentStart();
            }
            final Method method = interceptorContext.getMethod();
            final Interceptor interceptor = viewInterceptors.get(method);
            return interceptor.processInvocation(interceptorContext);
        }

        public Component getComponent() {
            return component;
        }

        @Override
        public Class getProxyClass() {
            return proxyFactory.defineClass();
        }

        @Override
        public Class getViewClass() {
            return viewClass;
        }

        @Override
        public Set getViewMethods() {
            return viewInterceptors.keySet();
        }

        @Override
        public Method getMethod(final String name, final String descriptor) {
            Method method = this.methods.get(new MethodDescription(name, descriptor));
            if (method == null) {
                throw EeLogger.ROOT_LOGGER.viewMethodNotFound(name, descriptor, viewClass, component.getComponentClass());
            }
            return method;
        }

        @Override
        public  T getPrivateData(final Class clazz) {
            return (T) privateData.get(clazz);
        }

        @Override
        public boolean isAsynchronous(final Method method) {
            return asyncMethods.contains(method);
        }

        @Override
        public String toString() {
            return "Component view " + viewClass + " for component "
                    + component.getComponentClass();
        }

        private final class MethodDescription {
            private final String name;
            private final String descriptor;

            public MethodDescription(final String name, final String descriptor) {
                this.name = name;
                this.descriptor = descriptor;
            }

            @Override
            public boolean equals(final Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;

                final MethodDescription that = (MethodDescription) o;

                if (!descriptor.equals(that.descriptor)) return false;
                if (!name.equals(that.name)) return false;

                return true;
            }

            @Override
            public int hashCode() {
                int result = name.hashCode();
                result = 31 * result + descriptor.hashCode();
                return result;
            }
        }
    }

    private class DefaultViewInstanceFactory implements ViewInstanceFactory {

        public ManagedReference createViewInstance(final ComponentView componentView, final Map contextData) throws Exception {

            final Object proxy;
            final Component component = componentView.getComponent();
            final ComponentClientInstance instance = new ComponentClientInstance();
            try {
                proxy = proxyFactory.newInstance(new ProxyInvocationHandler(clientInterceptors, instance, componentView));
            } catch (InstantiationException e) {
                InstantiationError error = new InstantiationError(e.getMessage());
                Throwable cause = e.getCause();
                if (cause != null) error.initCause(cause);
                throw error;
            } catch (IllegalAccessException e) {
                IllegalAccessError error = new IllegalAccessError(e.getMessage());
                Throwable cause = e.getCause();
                if (cause != null) error.initCause(cause);
                throw error;
            }

            InterceptorContext context = new InterceptorContext();
            context.putPrivateData(ComponentView.class, componentView);
            context.putPrivateData(Component.class, component);
            context.putPrivateData(ComponentClientInstance.class, instance);
            context.setContextData(new HashMap());
            for(Map.Entry entry : contextData.entrySet()) {
                context.putPrivateData(entry.getKey(), entry.getValue());
            }
            clientPostConstructInterceptor.processInvocation(context);
            instance.constructionComplete();

            return new ManagedReference() {

                @Override
                public void release() {
                    try {
                        InterceptorContext interceptorContext = new InterceptorContext();
                        interceptorContext.putPrivateData(ComponentView.class, componentView);
                        interceptorContext.putPrivateData(Component.class, component);
                        clientPreDestroyInterceptor.processInvocation(interceptorContext);
                    } catch (Exception e) {
                        ROOT_LOGGER.preDestroyInterceptorFailure(e, component.getComponentClass());
                    }
                }

                @Override
                public Object getInstance() {
                    return proxy;
                }
            };
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy