Merge pull request #553 from nacx/eventbus-module

Added EventBus module to configure the sync and async EventBus
This commit is contained in:
Adrian Cole 2012-04-10 08:48:50 -07:00
commit 9cd9e4feeb
8 changed files with 333 additions and 8 deletions

View File

@ -45,8 +45,6 @@ import org.jclouds.lifecycle.Closer;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -323,12 +321,6 @@ public class ExecutorServiceModule extends AbstractModule {
} }
@Provides
@Singleton
EventBus provideEventBus(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads){
return new AsyncEventBus(userThreads);
}
@Provides @Provides
@Singleton @Singleton
@Named(Constants.PROPERTY_USER_THREADS) @Named(Constants.PROPERTY_USER_THREADS)

View File

@ -0,0 +1,38 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.events.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.common.eventbus.EventBus;
/**
* Designates the module configures an {@link EventBus}.
*
* @author Ignasi Barrera
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfiguresEventBus {
}

View File

@ -0,0 +1,85 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.events.config;
import java.util.concurrent.ExecutorService;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.events.config.annotations.AsyncBus;
import org.jclouds.events.handlers.DeadEventLoggingHandler;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
/**
* Configures the {@link EventBus} to be used in the platform.
* <p>
* This class will provide an {@link AsyncEventBus} to be used to provide a basic pub/sub system for
* asynchronous operations.
*
* @author Ignasi Barrera
*
* @see ExecutorServiceModule
* @see AsyncEventBus
* @see EventBus
* @see AsyncBus
*/
@ConfiguresEventBus
public class EventBusModule extends AbstractModule {
/**
* Provides an {@link AsyncEventBus} that will use the configured executor service to dispatch
* events to subscribers.
*/
@Provides
@Singleton
AsyncEventBus provideAsyncEventBus(
@Named(Constants.PROPERTY_USER_THREADS) final ExecutorService executor,
final DeadEventLoggingHandler deadEventsHandler) {
AsyncEventBus asyncBus = new AsyncEventBus("jclouds-async-event-bus", executor);
asyncBus.register(deadEventsHandler);
return asyncBus;
}
/**
* Provides asynchronous {@link EventBus}.
*/
@Provides
@Singleton
EventBus provideSyncEventBus(final DeadEventLoggingHandler deadEventsHandler) {
EventBus syncBus = new EventBus("jclouds-sync-event-bus");
syncBus.register(deadEventsHandler);
return syncBus;
}
/**
* Configures the {@link EventBus} to be singleton and enables the {@link AsyncBus} annotation.
*/
@Override
protected void configure() {
bind(EventBus.class).annotatedWith(AsyncBus.class).to(AsyncEventBus.class);
}
}

View File

@ -0,0 +1,50 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.events.config.annotations;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import org.jclouds.events.config.EventBusModule;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
/**
* Used to configure {@link EventBus} injection, providing a flexible way to inject the
* {@link AsyncEventBus}.
*
* @author Ignasi Barrera
*
* @see EventBusModule
*/
@Target({ANNOTATION_TYPE, FIELD, PARAMETER})
@Retention(RUNTIME)
@Qualifier
public @interface AsyncBus {
}

View File

@ -0,0 +1,52 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.events.handlers;
import javax.annotation.Resource;
import javax.inject.Singleton;
import org.jclouds.logging.Logger;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
/**
* Default handler for dead events.
* <p>
* It simply logs all dead events to allow debugging and troubleshooting.
*
* @author Ignasi Barrera
*/
@Singleton
public class DeadEventLoggingHandler
{
@Resource
private Logger logger = Logger.NULL;
/**
* Due to <a href="http://code.google.com/p/guava-libraries/issues/detail?id=783">Guava Issue
* 786</a> {@link #handleDeadEvent(DeadEvent)} is marked <code>final</code>to avoid having
* duplicate events.
*/
@Subscribe
public final void handleDeadEvent(DeadEvent deadEvent) {
logger.warn("detected dead event %s", deadEvent.getEvent());
}
}

View File

@ -35,6 +35,8 @@ import org.jclouds.concurrent.MoreExecutors;
import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.concurrent.config.ConfiguresExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.events.config.ConfiguresEventBus;
import org.jclouds.events.config.EventBusModule;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
@ -109,6 +111,7 @@ public class RestContextBuilder<S, A> {
addHttpModuleIfNeededAndNotPresent(modules); addHttpModuleIfNeededAndNotPresent(modules);
ifHttpConfigureRestOtherwiseGuiceClientFactory(modules); ifHttpConfigureRestOtherwiseGuiceClientFactory(modules);
addExecutorServiceIfNotPresent(modules); addExecutorServiceIfNotPresent(modules);
addEventBusIfNotPresent(modules);
addCredentialStoreIfNotPresent(modules); addCredentialStoreIfNotPresent(modules);
modules.add(new LifeCycleModule()); modules.add(new LifeCycleModule());
modules.add(new BindPropertiesToAnnotations()); modules.add(new BindPropertiesToAnnotations());
@ -212,6 +215,19 @@ public class RestContextBuilder<S, A> {
modules.add(new RestClientModule<S, A>(syncClientType, asyncClientType)); modules.add(new RestClientModule<S, A>(syncClientType, asyncClientType));
} }
@VisibleForTesting
protected void addEventBusIfNotPresent(List<Module> modules) {
if (!any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(ConfiguresEventBus.class);
}
}
)) {
modules.add(new EventBusModule());
}
}
@VisibleForTesting @VisibleForTesting
protected void addExecutorServiceIfNotPresent(List<Module> modules) { protected void addExecutorServiceIfNotPresent(List<Module> modules) {
if (!any(modules, new Predicate<Module>() { if (!any(modules, new Predicate<Module>() {

View File

@ -0,0 +1,80 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.events.config;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import org.jclouds.Constants;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.events.config.annotations.AsyncBus;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
/**
* Unit tests for the {@link EventBusModule} class.
*
* @author Ignasi Barrera
*/
@Test(groups = "unit")
public class EventBusModuleTest
{
private Injector injector;
@BeforeMethod
public void setup() {
ExecutorServiceModule executorServiceModule = new ExecutorServiceModule() {
@Override
protected void configure() {
bindConstant().annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).to(1);
bindConstant().annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).to(1);
super.configure();
}
};
EventBusModule eventBusModule = new EventBusModule();
injector = Guice.createInjector(executorServiceModule, eventBusModule);
}
public void testAsyncExecutorIsProvided() {
assertNotNull(injector.getInstance(AsyncEventBus.class));
}
public void testAsyncAnnotatedEventBusIsBound() {
Key<EventBus> eventBusKey = Key.get(EventBus.class, AsyncBus.class);
EventBus eventBus = injector.getInstance(eventBusKey);
assertNotNull(eventBus);
assertTrue(eventBus instanceof AsyncEventBus);
}
public void testEventBusIsSingleton() {
EventBus eventBus1 = injector.getInstance(EventBus.class);
EventBus eventBus2 = injector.getInstance(EventBus.class);
assertTrue(eventBus1 == eventBus2);
}
}

View File

@ -26,6 +26,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.events.config.EventBusModule;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
@ -78,6 +79,17 @@ public class RestContextBuilderTest {
assertEquals(modules.remove(0), module); assertEquals(modules.remove(0), module);
} }
@Test
public void testAddEventBusModuleIfNotPresent() {
List<Module> modules = new ArrayList<Module>();
EventBusModule module = new EventBusModule();
modules.add(module);
new RestContextBuilder<String, String>(String.class, String.class, new Properties())
.addEventBusIfNotPresent(modules);
assertEquals(modules.size(), 1);
assertEquals(modules.remove(0), module);
}
@Test @Test
public void testAddExecutorServiceModuleIfNotPresent() { public void testAddExecutorServiceModuleIfNotPresent() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();