Java tutorial
/* * 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 com.github.javawithmarcus.wicket.cdi; import java.util.Arrays; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; import javax.inject.Inject; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.application.IComponentOnBeforeRenderListener; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.AbstractRequestCycleListener; import org.apache.wicket.request.cycle.IRequestCycleListener; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.cycle.RequestCycleListenerCollection; import org.apache.wicket.util.lang.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Configures CDI integration * * @author igor * @author jsarman */ @ApplicationScoped public class CdiConfiguration { private static final Logger logger = LoggerFactory.getLogger(CdiConfiguration.class); private static final String[] defaultIgnoredPackages = new String[] { "org.apache.wicket.markup", "org.apache.wicket.protocol.http", "org.apache.wicket.behavior", }; @Inject private AbstractCdiContainer container; @Inject private INonContextualManager nonContextualManager; @Inject private ConversationPropagator conversationPropagator; @Inject private DetachEventEmitter detachEventEmitter; @Inject private BehaviorInjector behaviorInjector; @Inject private ComponentInjector componentInjector; @Inject private SessionInjector sessionInjector; @Inject private ConversationManager conversationManager; @Inject private ConversationExpiryChecker conversationExpiryChecker; protected Map<String, ConfigurationParameters> parameters; /** * Not intended for public use. Use {@link #get()} */ public CdiConfiguration() { } @PostConstruct protected void init() { parameters = new TreeMap<>(); } /** * @return true if component injection is enabled or false otherwise. */ public boolean isInjectComponents() { return getApplicationParameters().isInjectComponents(); } /** * Flag to set if ComponentInjection is enabled. * <p/> * This method will throw IllegalStateException if called after configured. * * @param injectComponents * @return {@code this} for easy chaining * @deprecated Application Level Configuration replaced with {@link CdiWicketFilter} */ @Deprecated public CdiConfiguration setInjectComponents(boolean injectComponents) { ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { throw new IllegalStateException("Component Injection can only be changed before configure is called"); } params.setInjectComponents(injectComponents); return this; } /** * @return true if application injection is enabled or false otherwise. */ public boolean isInjectApplication() { return getApplicationParameters().isInjectApplication(); } /** * Flag to set if ApplicationInjection is enabled. * <p/> * This method will throw IllegalStateException if called after configured. * * @param injectApplication * @return {@code this} for easy chaining * @deprecated Application Level Configuration replaced with {@link CdiWicketFilter} */ @Deprecated public CdiConfiguration setInjectApplication(boolean injectApplication) { ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { throw new IllegalStateException("Application Injection can only be changed before configure is called"); } params.setInjectApplication(injectApplication); return this; } /** * @return true if session injection is enabled or false otherwise. */ public boolean isInjectSession() { return getApplicationParameters().isInjectSession(); } /** * Flag to set if SessionInjection is enabled. * <p/> * This method will throw IllegalStateException if called after configured. * * @param injectSession * @return {@code this} for easy chaining * @deprecated Application Level Configuration replaced with {@link CdiWicketFilter} */ @Deprecated public CdiConfiguration setInjectSession(boolean injectSession) { ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { throw new IllegalStateException("Session Injection can only be changed before configure is called"); } params.setInjectSession(injectSession); return this; } /** * @return true if behavior injection is enabled or false otherwise. */ public boolean isInjectBehaviors() { return getApplicationParameters().isInjectBehaviors(); } /** * Flag to set if BehaviorInjection is enabled. * <p/> * This method will throw IllegalStateException if called after configured. * * @param injectBehaviors * @return {@code this} for easy chaining * @deprecated Application Level Configuration replaced with {@link CdiWicketFilter} */ @Deprecated public CdiConfiguration setInjectBehaviors(boolean injectBehaviors) { ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { throw new IllegalStateException("Behavior Injection can only be changed before configure is called"); } params.setInjectBehaviors(injectBehaviors); return this; } /** * @return Active {@link IConversationPropagation}. */ @Produces @Propagation public IConversationPropagation getPropagation() { return getApplicationParameters().getPropagation(); } /** * @return {@link Boolean#TRUE} if auto conversation management is enabled. */ @Produces @Auto public Boolean isAutoConversationManagement() { return getApplicationParameters().isAutoConversationManagement(); } /** * Toggles automatic conversation management feature. * <p/> * Automatic conversation management controls the lifecycle of the conversation based on presence of components * implementing the {@link ConversationalComponent} interface. If such components are found in the page a * conversation is marked persistent, and if they are not the conversation is marked transient. This greatly * simplifies the management of conversation lifecycle. * <p/> * ConversationManagement can also be enable per Conversation after configured. Once the CdiConfiguration is * configured this call is passed to {@link ConversationManager#setManageConversation(boolean) } for the * ConversationManager in the current ConversationScope. This allows for ConversationManagement per active * Conversation. * <p/> * * @param enabled * @return {@code this} for easy chaining */ public CdiConfiguration setAutoConversationManagement(boolean enabled) { ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { if (container.getCurrentConversation().isTransient()) { logger.warn( "Not setting AutoConversationManagement because the conversation context is transient."); return this; } conversationManager.setManageConversation(enabled); } else { params.setAutoConversationManagement(enabled); } return this; } /** * Method to set the ConversationPropagation. * <p/> * * @param propagation * @return {@code this} for easy chaining */ public CdiConfiguration setPropagation(IConversationPropagation propagation) { Args.notNull(propagation, "propagation"); ConfigurationParameters params = getApplicationParameters(); if (params.isConfigured()) { if (container.getCurrentConversation().isTransient()) { logger.warn("Not setting propagation because the conversation context is transient."); return this; } conversationManager.setPropagation(propagation); } else { params.setPropagation(propagation); } return this; } /** * @return Retrieve the {@link INonContextualManager} */ public INonContextualManager getNonContextualManager() { return nonContextualManager; } /** * @return true if configured for Application */ public boolean isConfigured() { return getApplicationParameters().isConfigured(); } /** * Ignore package producer * * @return Packages to ignore. */ @Produces @IgnoreList private String[] getPackagesToIgnore() { ConfigurationParameters params = getApplicationParameters(); String[] ignore = new String[params.getIgnoredPackages().size()]; return params.getIgnoredPackages().toArray(ignore); } /** * Allows for addition of individual classes to be ignored during injection. * * @param classes * @return {@code this} for easy chaining */ public CdiConfiguration addClassesToIgnore(Class<?>... classes) { if (classes != null && classes.length > 0) { ConfigurationParameters params = getApplicationParameters(); for (Class<?> clazz : classes) { params.getIgnoredPackages().add(clazz.getName()); } } return this; } /** * Remove one or more Classes from Ignore list * * @param classes * @return {@code this} for easy chaining */ public CdiConfiguration removeClassesToIgnore(Class<?>... classes) { if (classes != null && classes.length > 0) { ConfigurationParameters params = getApplicationParameters(); for (Class<?> clazz : classes) { params.getIgnoredPackages().remove(clazz.getName()); } } return this; } /** * Allows for addition of one or more packages to be ignored during injection. * * @param packageNames * @return {@code this} for easy chaining */ public CdiConfiguration addPackagesToIgnore(String... packageNames) { if (packageNames != null && packageNames.length > 0) { getApplicationParameters().getIgnoredPackages().addAll(Arrays.asList(packageNames)); } return this; } /** * Remove one or more Package from Ignore list * * @param packageNames * @return {@code this} for easy chaining */ public CdiConfiguration removePackagesToIgnore(String... packageNames) { if (packageNames != null && packageNames.length > 0) { getApplicationParameters().getIgnoredPackages().removeAll(Arrays.asList(packageNames)); } return this; } protected ConfigurationParameters getApplicationParameters() { ConfigurationParameters params = parameters.get(Application.get().getApplicationKey()); if (params == null) { try { Application app = Application.get(); if (app.getApplicationKey() == null) { throw new WicketRuntimeException(); } params = new ConfigurationParameters(); parameters.put(app.getApplicationKey(), params); } catch (WicketRuntimeException wre) { throw new IllegalStateException("Application is not ready."); } } return params; } /** * Configures the specified application. This method allows for CdiConfiguration to be setup at the Application * Level. Use the {@link CdiWicketFilter} as the filterClass or add the {@link CdiWebApplicationFactory} to the * Standard WicketFilter with init-param applicationFactoryClassName for setup during Application Initialization. * This allows for Injected classes in the WebApplication to be ready before init() is called. * * @param application * @deprecated Application Level Configuration replaced with {@link CdiWicketFilter} */ @Deprecated public void configure(Application application) { ConfigurationParameters params = getApplicationParameters(); configure(application.getApplicationKey(), application, params); } protected synchronized void configure(String appKey, Application application, ConfigurationParameters params) { if (parameters.containsKey(appKey)) { params = parameters.get(appKey); if (params.isConfigured()) { throw new IllegalStateException("Cannot configure CdiConfiguration multiple times"); } } else { parameters.put(appKey, params); } params.getIgnoredPackages().addAll(Arrays.asList(defaultIgnoredPackages)); RequestCycleListenerCollection listeners = new RequestCycleListenerCollection(); application.getRequestCycleListeners().add(listeners); // enable conversation propagation if (params.getPropagation() != ConversationPropagation.NONE) { enablePropagation(params, application); } // enable detach event listeners.add(detachEventEmitter); // inject application instance if (params.isInjectApplication()) { nonContextualManager.postConstruct(application); } // enable injection of various framework components if (params.isInjectSession()) { application.getSessionListeners().add(sessionInjector); } if (params.isInjectComponents()) { application.getComponentInstantiationListeners().add(componentInjector); } if (params.isInjectBehaviors()) { application.getBehaviorInstantiationListeners().add(behaviorInjector); } // enable cleanup application.getApplicationListeners().add(new CdiShutdownCleaner(params.isInjectApplication())); params.setConfigured(true); } /** * Convenience Method to get an Injected Instance of CdiConfiguration programmatically. * * @return {@link CdiConfiguration} instance. */ public static CdiConfiguration get() { BeanManager beanManager = CDI.current().getBeanManager(); Iterator<Bean<?>> iter = beanManager.getBeans(CdiConfiguration.class).iterator(); if (!iter.hasNext()) { throw new IllegalStateException("CDI BeanManager cannot find CdiConfiguration"); } @SuppressWarnings("unchecked") Bean<CdiConfiguration> bean = (Bean<CdiConfiguration>) iter.next(); CreationalContext<CdiConfiguration> ctx = beanManager.createCreationalContext(bean); return (CdiConfiguration) beanManager.getReference(bean, CdiConfiguration.class, ctx); } private void enablePropagation(ConfigurationParameters params, Application application) { disablePropagation(params); // Force remove active listeners if any IRequestCycleListener requestCycleListener = conversationPropagator;// new RequestCycleListenerWrapper(); application.getRequestCycleListeners().add(requestCycleListener); params.setActiveRequestCycleListener(requestCycleListener); IComponentOnBeforeRenderListener componentOnBeforeRenderListener = new ComponentOnBeforeRenderListenerWrapper(); application.getComponentPreOnBeforeRenderListeners().add(componentOnBeforeRenderListener); params.setActiveComponentOnBeforeRenderListener(componentOnBeforeRenderListener); } private void disablePropagation(ConfigurationParameters params) { IRequestCycleListener requestCycleListener = params.getActiveRequestCycleListener(); if (requestCycleListener != null) { Application.get().getRequestCycleListeners().remove(requestCycleListener); params.setActiveRequestCycleListener(null); } IComponentOnBeforeRenderListener componentOnBeforeRenderListener = params .getActiveComponentOnBeforeRenderListener(); if (componentOnBeforeRenderListener != null) { Application.get().getComponentPreOnBeforeRenderListeners().remove(componentOnBeforeRenderListener); params.setActiveComponentOnBeforeRenderListener(null); } } /** * Wrapper for the Current ConversationPropagator which allows the removal of the listener. */ class RequestCycleListenerWrapper extends AbstractRequestCycleListener { @Override public void onEndRequest(RequestCycle cycle) { conversationPropagator.onEndRequest(cycle); } @Override public void onRequestHandlerScheduled(RequestCycle cycle, IRequestHandler handler) { conversationPropagator.onRequestHandlerScheduled(cycle, handler); } @Override public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) { conversationPropagator.onRequestHandlerResolved(cycle, handler); } @Override public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) { conversationPropagator.onRequestHandlerExecuted(cycle, handler); } @Override public void onUrlMapped(RequestCycle cycle, IRequestHandler handler, Url url) { conversationPropagator.onUrlMapped(cycle, handler, url); } } private class ComponentOnBeforeRenderListenerWrapper implements IComponentOnBeforeRenderListener { @Override public void onBeforeRender(Component component) { conversationExpiryChecker.onBeforeRender(component); } } }