/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.deployment; import static org.richfaces.deployment.CoreFeature.CONFIGURATION_SERVICE; import static org.richfaces.deployment.CoreFeature.DEPENDENCY_INJECTOR; import static org.richfaces.deployment.CoreFeature.RESOURCE_CODEC; import static org.richfaces.deployment.CoreFeature.RESOURCE_HANDLER; import static org.richfaces.deployment.CoreFeature.SERVICE_LOADER; import java.lang.reflect.Method; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.faces.component.UIOutput; import javax.faces.event.PostConstructApplicationEvent; import javax.faces.event.PreDestroyApplicationEvent; import javax.servlet.ServletContainerInitializer; import org.ajax4jsf.util.base64.Codec; import org.jboss.arquillian.container.test.spi.RemoteLoadableExtension; import org.jboss.shrinkwrap.api.Filters; import org.jboss.shrinkwrap.api.GenericArchive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.Asset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.importer.ExplodedImporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.descriptor.api.facesconfig20.WebFacesConfigDescriptor; import org.jboss.shrinkwrap.descriptor.api.webapp30.WebAppDescriptor; import org.richfaces.VersionBean; import org.richfaces.application.CoreConfiguration; import org.richfaces.application.DependencyInjector; import org.richfaces.application.DependencyInjectorImpl; import org.richfaces.application.Initializable; import org.richfaces.application.InitializationListener; import org.richfaces.application.MessageFactory; import org.richfaces.application.Module; import org.richfaces.application.ServiceException; import org.richfaces.application.ServiceLoader; import org.richfaces.application.ServiceTracker; import org.richfaces.application.ServicesFactory; import org.richfaces.application.ServicesFactoryImpl; import org.richfaces.application.Uptime; import org.richfaces.application.configuration.ConfigurationItem; import org.richfaces.application.configuration.ConfigurationItemSource; import org.richfaces.application.configuration.ConfigurationItemsBundle; import org.richfaces.application.configuration.ConfigurationService; import org.richfaces.application.configuration.ConfigurationServiceHelper; import org.richfaces.application.configuration.ConfigurationServiceImpl; import org.richfaces.application.configuration.ValueExpressionHolder; import org.richfaces.application.push.MessageDataSerializer; import org.richfaces.application.push.MessageException; import org.richfaces.application.push.PushContext; import org.richfaces.application.push.PushContextFactory; import org.richfaces.application.push.PushContextInitializationException; import org.richfaces.application.push.Request; import org.richfaces.application.push.Session; import org.richfaces.application.push.SessionFactory; import org.richfaces.application.push.SessionManager; import org.richfaces.application.push.SessionSubscriptionEvent; import org.richfaces.application.push.SessionUnsubscriptionEvent; import org.richfaces.application.push.Topic; import org.richfaces.application.push.TopicEvent; import org.richfaces.application.push.TopicKey; import org.richfaces.application.push.TopicListener; import org.richfaces.application.push.TopicsContext; import org.richfaces.application.push.impl.AbstractTopic; import org.richfaces.application.push.impl.DefaultMessageDataSerializer; import org.richfaces.application.push.impl.MessageDataScriptString; import org.richfaces.application.push.impl.PushContextFactoryImpl; import org.richfaces.application.push.impl.PushContextImpl; import org.richfaces.application.push.impl.RequestImpl; import org.richfaces.application.push.impl.SessionFactoryImpl; import org.richfaces.application.push.impl.SessionImpl; import org.richfaces.application.push.impl.SessionManagerImpl; import org.richfaces.application.push.impl.SessionQueue; import org.richfaces.application.push.impl.TopicImpl; import org.richfaces.application.push.impl.TopicsContextImpl; import org.richfaces.application.push.impl.jms.JMSTopicsContextImpl; import org.richfaces.cache.Cache; import org.richfaces.component.UIResource; import org.richfaces.component.UITransient; import org.richfaces.el.GenericsIntrospectionService; import org.richfaces.javascript.JavaScriptService; import org.richfaces.l10n.BundleLoader; import org.richfaces.l10n.InterpolationException; import org.richfaces.l10n.MessageBundle; import org.richfaces.l10n.MessageInterpolator; import org.richfaces.log.JavaLogger; import org.richfaces.log.LogFactory; import org.richfaces.log.Logger; import org.richfaces.log.RichfacesLogger; import org.richfaces.renderkit.AjaxDataSerializer; import org.richfaces.renderkit.html.ResourceLibraryRenderer; import org.richfaces.renderkit.html.ResourceRenderer; import org.richfaces.resource.DefaultResourceCodec; import org.richfaces.resource.ResourceCodec; import org.richfaces.resource.ResourceHandlerImpl; import org.richfaces.resource.ResourceLibrary; import org.richfaces.resource.ResourceLibraryFactory; import org.richfaces.resource.ResourceLibraryFactoryImpl; import org.richfaces.resource.StaticResourceLibrary; import org.richfaces.resource.external.MappedResourceFactory; import org.richfaces.resource.external.MappedResourceFactoryImpl; import org.richfaces.resource.external.ResourceTracker; import org.richfaces.resource.external.ResourceTrackerImpl; import org.richfaces.shrinkwrap.descriptor.PropertiesAsset; import org.richfaces.skin.SkinFactory; import org.richfaces.util.LRUMapTest; import org.richfaces.util.PropertiesUtil; import org.richfaces.wait.Condition; import org.richfaces.wait.Wait; import org.richfaces.wait.WaitTimeoutException; import org.richfaces.webapp.AbstractServletContainerInitializer; import org.richfaces.webapp.PushFilter; import org.richfaces.webapp.PushHandlerFilter; import org.richfaces.webapp.PushServlet; import org.richfaces.webapp.PushServletContainerInitializer; import org.richfaces.webapp.ServletConfigDefaultsFacade; import com.google.common.base.Function; /** * Provides base for Core test deployment * * @author Lukas Fryc */ public class CoreDeployment extends BaseDeployment { private static final String TESTING_MODULE = "META-INF/richfaces/testing-module.properties"; private PropertiesAsset testingModules = new PropertiesAsset(); private EnumSet<CoreFeature> addedFeatures = EnumSet.noneOf(CoreFeature.class); /** * Constructs base Core deployment with dependencies, base classes and utilities. */ public CoreDeployment(Class<?> testClass) { this(testClass == null ? null : testClass.getSimpleName()); } public CoreDeployment(String archiveName) { super(archiveName); this.withWholeCore(); this.withBaseClasses().withUtilities().withLogging(); this.withDisabledControlSkinning(); // this.withArquillianExtensions().withWaiting(); } /* * (non-Javadoc) * * @see org.richfaces.deployment.Deployment#getFinalArchive() */ @Override public WebArchive getFinalArchive() { WebArchive finalArchive = super.getFinalArchive(); finalArchive.addManifest(); Asset manifest = finalArchive.get("META-INF/MANIFEST.MF").getAsset(); finalArchive.addAsResource(manifest, "META-INF/MANIFEST.MF"); // add testing modules list finalArchive.addAsResource(testingModules, TESTING_MODULE); return finalArchive; } /** * Avoids to load given feature several times. * * Adds given {@link CoreFeature} to the list of loaded features. * * Returns whether the given feature was already loaded. * * @param feature a feature to load * @return true if a given feature was already loaded; false otherwise */ private boolean addFeature(CoreFeature feature) { boolean alreadyAdded = addedFeatures.contains(feature); addedFeatures.add(feature); return alreadyAdded; } /** * Adds base classes which are necessary for each Core test */ public CoreDeployment withBaseClasses() { archive().addClasses(VersionBean.class); return this; } /** * Adds all utility classes */ public CoreDeployment withUtilities() { archive().addPackages(true, "org.richfaces.util"); // remove unnecessary imported test class archive().deleteClass(LRUMapTest.class); return this; } /** * Disable control skinning to remove warnings about not found resources */ public CoreDeployment withDisabledControlSkinning() { this.webXml(new Function<WebAppDescriptor, WebAppDescriptor>() { public WebAppDescriptor apply(WebAppDescriptor descriptor) { descriptor.getOrCreateContextParam().paramName("org.richfaces.enableControlSkinning") .paramValue("false"); return descriptor; } }); return this; } /** * Adds expression language classes */ public CoreDeployment withEL() { archive().addPackages(true, "org.richfaces.el"); return this; } /** * Adds {@link ConfigurationService} service */ public CoreDeployment withConfigurationService() { if (addFeature(CONFIGURATION_SERVICE)) { return this; } withService(ConfigurationService.class, ConfigurationServiceImpl.class); archive() .addClasses(ConfigurationService.class, ConfigurationServiceImpl.class) .addClasses(ConfigurationServiceHelper.class) .addClasses(ConfigurationItem.class, ConfigurationItemsBundle.class, ConfigurationItemSource.class) .addClasses(ValueExpressionHolder.class); return this; } /** * Adds {@link DependencyInjector} service */ public CoreDeployment withDependencyInjector() { if (addFeature(DEPENDENCY_INJECTOR)) { return this; } withService(DependencyInjector.class, DependencyInjectorImpl.class); archive().addClasses(DependencyInjectorImpl.class); return this; } /** * Adds core localization classes */ public CoreDeployment withLocalization() { if (addFeature(CoreFeature.LOCALIZATION)) { return this; } archive() .addClasses(BundleLoader.class, InterpolationException.class, MessageBundle.class, MessageInterpolator.class); return this; } /** * Adds {@link ResourceCodec} service * @return */ public CoreDeployment withResourceCodec() { if (addFeature(RESOURCE_CODEC)) { return this; } withService(ResourceCodec.class, DefaultResourceCodec.class); archive().addClasses(ResourceCodec.class, DefaultResourceCodec.class); return this; } /** * Adds Push and all its dependencies */ public CoreDeployment withPush() { if (addFeature(CoreFeature.PUSH)) { return this; } withService(PushContextFactory.class, PushContextFactoryImpl.class); StringAsset pushServletInitializer = new StringAsset(PushServletContainerInitializer.class.getName()); // According to ServletContainerInitializer, the service must be defined in own JAR JavaArchive servletInitializer = ShrinkWrap.create(JavaArchive.class, "push-classes.jar") .addAsResource(pushServletInitializer, "META-INF/services/" + ServletContainerInitializer.class.getName()) .addClasses(PushServlet.class, PushFilter.class, PushHandlerFilter.class, ServletConfigDefaultsFacade.class) .addClasses(PushServletContainerInitializer.class, AbstractServletContainerInitializer.class) .addClasses(TopicsContext.class, TopicsContextImpl.class) .addClasses(SessionManager.class, SessionManagerImpl.class, SessionQueue.class) .addClasses(SessionFactory.class, SessionFactoryImpl.class) .addClasses(Session.class, SessionImpl.class) .addClasses(Topic.class, TopicImpl.class, AbstractTopic.class, TopicKey.class, TopicEvent.class, TopicListener.class) .addClasses(MessageDataSerializer.class, DefaultMessageDataSerializer.class, MessageDataScriptString.class) .addClasses(Request.class, RequestImpl.class) .addClasses(PushContext.class, PushContextImpl.class) .addClasses(PushContextFactory.class, PushContextFactoryImpl.class) .addClasses(SessionSubscriptionEvent.class, SessionUnsubscriptionEvent.class) .addClasses(MessageException.class) .addClasses(JMSTopicsContextImpl.class) .addClasses(PushContextInitializationException.class); archive() .addAsLibrary(servletInitializer); addMavenDependency( "org.atmosphere:atmosphere-runtime"); return this; } /** * Adds Resource Libraries feature * @return */ public CoreDeployment withResourceLibraries() { if (addFeature(CoreFeature.RESOURCE_LIBRARIES)) { return this; } archive() .addClasses(ResourceLibrary.class, ResourceLibraryFactory.class, ResourceLibraryFactoryImpl.class) .addClasses(ResourceLibraryRenderer.class, StaticResourceLibrary.class) .addClasses(ResourceRenderer.class, UIResource.class, UITransient.class); withService(ResourceLibraryFactory.class, ResourceLibraryFactoryImpl.class); facesConfig(new Function<WebFacesConfigDescriptor, WebFacesConfigDescriptor>() { @Override public WebFacesConfigDescriptor apply(WebFacesConfigDescriptor input) { return input .getOrCreateRenderKit() .createRenderer() .componentFamily(UIOutput.COMPONENT_FAMILY) .rendererType(ResourceLibraryRenderer.RENDERER_TYPE) .rendererClass(ResourceLibraryRenderer.class.getName()) .up() .up(); } }); return this; } /** * Adds core logging classes */ public CoreDeployment withLogging() { if (addFeature(CoreFeature.LOGGING)) { return this; } archive() // log .addClasses(RichfacesLogger.class, JavaLogger.class, LogFactory.class, Logger.class); this.withLocalization(); return this; } /** * Adds {@link ResourceHandlerImpl} and its dependencies */ public CoreDeployment withResourceHandler() { if (addFeature(RESOURCE_HANDLER)) { return this; } archive() // resource .addPackage("org.richfaces.resource") .addPackage("org.richfaces.resource.css") .addPackage("org.richfaces.resource.external") // codec .addClasses(Codec.class) // cache .addClasses(Cache.class); withService(MappedResourceFactory.class, MappedResourceFactoryImpl.class); withService(ResourceTracker.class, ResourceTrackerImpl.class); facesConfig(new Function<WebFacesConfigDescriptor, WebFacesConfigDescriptor>() { @Override public WebFacesConfigDescriptor apply(WebFacesConfigDescriptor facesConfig) { return facesConfig .getOrCreateApplication() .resourceHandler(ResourceHandlerImpl.class.getName()) .up(); } }); return this; } public CoreDeployment withWholeCore() { JavaArchive coreArchive = ShrinkWrap.create(JavaArchive.class, "dynamic-richfaces-core.jar"); coreArchive.merge(ShrinkWrap.create(GenericArchive.class).as(ExplodedImporter.class) .importDirectory("target/classes/").as(GenericArchive.class), "/", Filters.includeAll()); archive().addAsLibrary(coreArchive); return this; } public void withA4jComponents() { addMavenDependency("org.richfaces:richfaces-a4j:4.5.18-SNAPSHOT"); excludeMavenDependency("richfaces-core"); } public void withRichComponents() { addMavenDependency("org.richfaces:richfaces:4.5.18-SNAPSHOT"); excludeMavenDependency("richfaces-core"); } public void withWholeFramework() { withRichComponents(); } /** * Adds {@link CoreTestingRemoteExtension} (for testing Core with Arquillian) and its dependencies * @return */ private CoreDeployment withArquillianExtensions() { archive().addAsServiceProvider(RemoteLoadableExtension.class, CoreTestingRemoteExtension.class); archive().addClasses(CoreTestingRemoteExtension.class, CoreServicesEnricher.class); archive().addClasses(ConfigurationService.class, SkinFactory.class, AjaxDataSerializer.class, ResourceCodec.class, Cache.class, Uptime.class, DependencyInjector.class, MessageFactory.class, ResourceLibraryFactory.class, PushContextFactory.class, JavaScriptService.class, GenericsIntrospectionService.class, ResourceTracker.class, MappedResourceFactory.class); return this; } /** * Adds base classes for using waiting with timeouts */ private CoreDeployment withWaiting() { archive().addClasses(Condition.class, Wait.class, WaitTimeoutException.class); return this; } /** * Adds service for given type and implementation */ public CoreDeployment withService(Class<?> type, Class<?> implementation) { withServiceTracker(); testingModules = testingModules.key(type.getName()).value(implementation.getName()); return this; } /** * Adds {@link ServiceTracker}, its initializer and module which is able to load services added by * {@link #withService(Class, Class)}. */ public CoreDeployment withServiceTracker() { if (addFeature(SERVICE_LOADER)) { return this; } // add base dependencies archive() .addClasses(InitializationListener.class, CoreConfiguration.class, ServicesFactory.class, ServicesFactoryImpl.class) .addClasses(ServiceLoader.class, ServiceException.class, ServiceTracker.class, Module.class, Initializable.class); withConfigurationService(); // register testing module StringAsset moduleAsset = new StringAsset(TestingModule.class.getName()); archive().addClass(TestingModule.class).addAsResource(moduleAsset, "META-INF/services/" + Module.class.getName()); // register initialization listener archive().addClass(TestingInitializationListener.class); facesConfig(new Function<WebFacesConfigDescriptor, WebFacesConfigDescriptor>() { @Override public WebFacesConfigDescriptor apply(WebFacesConfigDescriptor facesConfig) { return facesConfig.getOrCreateApplication().createSystemEventListener() .systemEventClass(PostConstructApplicationEvent.class.getName()) .systemEventListenerClass(TestingInitializationListener.class.getName()).up() .createSystemEventListener().systemEventClass(PreDestroyApplicationEvent.class.getName()) .systemEventListenerClass(TestingInitializationListener.class.getName()).up().up(); } }); return this; } /** * Initialization listener which does not load any module by default, and delegates rather to modules added by * {@link TestingModule}. */ public static class TestingInitializationListener extends InitializationListener { @Override protected void addDefaultModules(List<Module> modules) { } } /** * Loads the list of services from properties file. * * You can add those using {@link CoreDeployment#withService(Class, Class)}. */ public static class TestingModule implements Module { @Override public void configure(ServicesFactory factory) { ClassLoader classLoader = Module.class.getClassLoader(); Map<String, String> map = PropertiesUtil.loadProperties(TESTING_MODULE); for (Entry<String, String> entry : map.entrySet()) { try { Class<?> factoryClass = classLoader.loadClass(entry.getKey()); Class<?> implementationClass = classLoader.loadClass(entry.getValue()); Object implementationInstance = implementationClass.newInstance(); Method method = factory.getClass().getMethod("setInstance", new Class<?>[] { Class.class, Object.class }); method.invoke(factory, factoryClass, implementationInstance); } catch (Exception e) { throw new IllegalStateException(e); } } } } }