/* * Copyright 2007 Tim Peierls * * Licensed 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.directwebremoting.guice; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.directwebremoting.AjaxFilter; import org.directwebremoting.extend.Converter; import org.directwebremoting.guice.util.AbstractModule; import org.directwebremoting.util.LocalUtil; import com.google.inject.binder.ConstantBindingBuilder; import com.google.inject.binder.LinkedBindingBuilder; import static java.util.Arrays.*; import static org.directwebremoting.guice.ParamName.*; /** * An extension of the enhanced {@link AbstractModule} from the util * subpackage that adds DWR configuration methods when used in conjunction * with {@link DwrGuiceServlet}. * @author Tim Peierls [tim at peierls dot net] */ public abstract class AbstractDwrModule extends AbstractModule { /** * Implement this method to configure Guice bindings for a DWR-based * web application. */ @Override protected abstract void configure(); /** * Call this method before configuration to explicitly determine the behavior * of {@link #bindDwrScopes()}. If not called, the default behavior is to bind * the potentially conflicting types only if the Guice ServletModule is not found * in the classloader. That is usually the right behavior; it should not often be * necessary to call this method. * @param newBindPotentiallyConflictingTypes whether to bind request, response, and * session types to DWR scopes without qualifying with an annotation */ protected final void bindPotentiallyConflictingTypes(boolean newBindPotentiallyConflictingTypes) { this.bindPotentiallyConflictingTypes = newBindPotentiallyConflictingTypes; } /** * Configure DWR scopes and bindings for servlet-related types. * If {@link #bindPotentiallyConflictingTypes} has been * called previously for this module, this method is equivalent * to calling {@link #bindDwrScopes(boolean) bindDwrScopes} * with the value passed to {@link #bindPotentiallyConflictingTypes}. * Otherwise this module will include bindings that might conflict * with those provided by Guice's ServletModule <strong>only</strong> * if ServletModule is not found in the current class loader. * <p>Idempotent within current thread.</p> */ protected void bindDwrScopes() { if (this.bindPotentiallyConflictingTypes == null) { bindDwrScopes(!guiceServletModuleExists()); } else { bindDwrScopes(this.bindPotentiallyConflictingTypes); } } /** * Configure DWR scopes and bindings for servlet-related types, * specifying explicitly whether to include bindings that might * conflict with those provided by Guice's ServletModule. * The {@link #bindDwrScopes() variant} of this method that takes * no arguments usually does the right; it should not often be * necessary to call this method. * <p>Idempotent within current thread.</p> * @param newBindPotentiallyConflictingTypes whether to bind request, response, * and session types (risking conflict with Guice) */ protected void bindDwrScopes(boolean newBindPotentiallyConflictingTypes) { if (!boundDwrScopes.get()) { boundDwrScopes.set(true); install(new DwrGuiceServletModule(newBindPotentiallyConflictingTypes)); } } /** * Call this method in * {@link org.directwebremoting.guice.AbstractDwrModule#configure configure} * to specify classes that DWR should scan for annotations. * @param classes the classes to be scanned for DWR-specific annotations */ protected void bindAnnotatedClasses(Class<?>... classes) { bind(List.class) .annotatedWith(new InitParamImpl(CLASSES, unique.incrementAndGet())) .toInstance(asList(classes)); } /** * Creates a binding to {@code type} that is used as the target of a * remote method call with the class's unqualified name as the script name. * * <p>Note: if you are scoping the result, don't rely on implicit binding. * Instead, link the type to itself explicitly. For example, * <pre> * bindRemoted(ConcreteService.class) * .to(ConcreteService.class) // this line is required * .in(DwrScopes.SESSION); * </pre> * This could be considered a bug. * @param type the type to bind as a target for remote method calls */ protected <T> LinkedBindingBuilder<T> bindRemoted(Class<T> type) { return bind(type) .annotatedWith(new RemotedImpl()); } /** * Creates a binding to a type that is used as the target of a * remote method call with the given {@code scriptName}. * * <p>Note: if you are scoping the result, don't rely on implicit binding. * Instead, link the type to itself explicitly. For example, * <pre> * bindRemotedAs("Mixer", ConcreteService.class) * .to(ConcreteService.class) // this line is required * .in(DwrScopes.SESSION); * </pre> * This could be considered a bug. * @param scriptName the name by which the target type will be known to script callers * @param type the type to bind as a target for remote method calls */ protected <T> LinkedBindingBuilder<T> bindRemotedAs(String scriptName, Class<T> type) { return bind(type) .annotatedWith(new RemotedImpl(scriptName)); } /** * Creates a binding for a conversion for types with names matching * {@code match}. * @param match the string describing which types to convert */ protected LinkedBindingBuilder<Converter> bindConversion(String match) { return bind(Converter.class) .annotatedWith(new ConvertingImpl(match)); } /** * Creates a binding for a conversion for {@code type}. * @param type the type to be converted */ protected LinkedBindingBuilder<Converter> bindConversion(Class<?> type) { return bind(Converter.class) .annotatedWith(new ConvertingImpl(type)); } /** * Creates a binding for a conversion for {@code type} using an existing * conversion for {@code impl}, which must be assignable to {@code type}. * The check for an existing conversion happens at run-time. * @param type the type to be converted * @param impl a type for which a conversion is already defined */ protected <T> void bindConversion(Class<T> type, Class<? extends T> impl) { bind(Converter.class) .annotatedWith(new ConvertingImpl(type, impl)) .to(InternalConverter.class); // never used, subverted by InternalConverterManager } /** * Creates a binding for an Ajax filter for the script named * {@code scriptName}. * @param scriptName the script to filter */ protected LinkedBindingBuilder<AjaxFilter> bindFilter(String scriptName) { return bind(AjaxFilter.class) .annotatedWith(new FilteringImpl(scriptName, unique.incrementAndGet())); } /** * Creates a binding for a global Ajax filter. */ protected LinkedBindingBuilder<AjaxFilter> bindGlobalFilter() { return bind(AjaxFilter.class) .annotatedWith(new FilteringImpl("", unique.incrementAndGet())); } /** * Call this method in * {@link org.directwebremoting.guice.AbstractDwrModule#configure configure} * to create a binding for a DWR parameter. * @param paramName a parameter name supported by DWR */ protected ConstantBindingBuilder bindParameter(ParamName paramName) { return bindConstant() .annotatedWith(new InitParamImpl(paramName)); } /** * Used to determine what value {@link #bindDwrScopes() bindDwrScopes()} * passes to {@link #bindDwrScopes(boolean) bindDwrScopes(boolean)}. * If null, the result of calling {@code !guiceServletModuleExists()} * is used. */ volatile Boolean bindPotentiallyConflictingTypes = null; private static final AtomicLong unique = new AtomicLong(); private static final ThreadLocal<Boolean> boundDwrScopes = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return false; } }; private static boolean guiceServletModuleExists() { try { LocalUtil.classForName("com.google.inject.servlet.ServletModule"); return true; } catch (ClassNotFoundException e) { return false; } } }