/* * Copyright 2012 Jason Miller * * 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 jj; import static java.lang.annotation.ElementType.*; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.inject.Qualifier; import javax.inject.Singleton; import jj.configuration.ConfigurationObjectBinder; import jj.conversion.Converter; import jj.engine.HostObject; import jj.http.server.ServableResource; import jj.http.server.ServableResourceBindingProcessor; import jj.http.server.websocket.WebSocketConnectionHost; import jj.http.server.websocket.WebSocketConnectionHostBindingProcessor; import jj.logging.LoggingBinder; import jj.logging.LoggingBinder.BindingBuilder; import jj.resource.AbstractResource; import jj.resource.ResourceBinder; import jj.resource.SimpleResourceCreator; import jj.script.Continuation; import jj.script.ContinuationProcessor; import jj.script.ContinuationProcessorBinder; import jj.server.APIModulePaths; import jj.server.APISpecPaths; import jj.server.AssetPaths; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.binder.LinkedBindingBuilder; import com.google.inject.multibindings.Multibinder; /** * <p> * Base module to register components into the JibbrJabbr system * * @author jason * */ public abstract class JJModule extends AbstractModule { // this annotation is used to ensure that no one can inject Set<Object> and // get a weird variety of server components, because that's just not a // sensible thing to do @Qualifier @Target(PARAMETER) @Retention(RetentionPolicy.RUNTIME) private @interface StartupListeners {} private ResourceBinder resources; private ContinuationProcessorBinder continuationProcessors; private LoggingBinder loggers; private ConfigurationObjectBinder configurationObjects; // nobody cares about this! but it's necessary // to make the startup work private Multibinder<Object> startupListeners; private Multibinder<Converter<?, ?>> converters; private Multibinder<HostObject> hostObjects; private Multibinder<String> assetPaths; private Multibinder<String> apiModulePaths; private Multibinder<String> apiSpecPaths; protected void bindStartupListener(Class<?> startupListenerClass) { assert startupListenerClass.isAnnotationPresent(Singleton.class) : "startup listeners must be singletons!"; if (startupListeners == null) { startupListeners = Multibinder.newSetBinder(binder(), Object.class, StartupListeners.class); } startupListeners.addBinding().to(startupListenerClass).asEagerSingleton(); } protected void bindConverter(Class<? extends Converter<?, ?>> converterClass) { if (converters == null) { converters = Multibinder.newSetBinder(binder(), new TypeLiteral<Converter<?, ?>>() {}); } converters.addBinding().to(converterClass); } protected void bindHostObject(Class<? extends HostObject> hostObjectClass) { if (hostObjects == null) { hostObjects = Multibinder.newSetBinder(binder(), HostObject.class); } hostObjects.addBinding().to(hostObjectClass); } protected void bindAssetPath(String path) { assert path != null && path.startsWith("/") : "path must be present and start with /"; if (assetPaths == null) { assetPaths = Multibinder.newSetBinder(binder(), String.class, AssetPaths.class); } assetPaths.addBinding().toInstance(path); } protected void bindAPIModulePath(String path) { assert path != null && path.startsWith("/") : "path must be present and start with /"; if (apiModulePaths == null) { apiModulePaths = Multibinder.newSetBinder(binder(), String.class, APIModulePaths.class); } apiModulePaths.addBinding().toInstance(path); } protected void bindAPISpecPath(String path) { assert path != null && path.startsWith("/") : "path must be present and start with /"; if (apiSpecPaths == null) { apiSpecPaths = Multibinder.newSetBinder(binder(), String.class, APISpecPaths.class); } apiSpecPaths.addBinding().toInstance(path); } protected <T extends AbstractResource<A>, A> LinkedBindingBuilder<SimpleResourceCreator<T, A>> bindCreationOf(Class<T> resourceClass) { if (resources == null) { resources = new ResourceBinder(binder()) .addResourceBindingProcessor(ServableResource.class, new ServableResourceBindingProcessor(binder())) .addResourceBindingProcessor(WebSocketConnectionHost.class, new WebSocketConnectionHostBindingProcessor(binder())); } return resources.of(resourceClass); } protected LinkedBindingBuilder<ContinuationProcessor> bindContinuationProcessingOf(Class<? extends Continuation> continuationClass) { if (continuationProcessors == null) { continuationProcessors = new ContinuationProcessorBinder(binder()); } return continuationProcessors.continuationOf(continuationClass); } protected void bindExecutor(Class<?> executor) { assert executor.isAnnotationPresent(Singleton.class); binder().bind(executor).asEagerSingleton(); } protected BindingBuilder bindLoggedEventsAnnotatedWith(Class<? extends Annotation> annotation) { if (loggers == null) { loggers = new LoggingBinder(binder()); } return loggers.annotatedWith(annotation); } protected void bindConfiguration(Class<?> configurationInterface) { if (configurationObjects == null) { configurationObjects = new ConfigurationObjectBinder(binder()); } configurationObjects.to(configurationInterface); } }