Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Event based structure introduced. Much cleaner.

i really need to start thinking in terms of tasks here :D
  • Loading branch information...
commit 4d8fb78f474e51fd73b5477becdac6d013202be2 1 parent 126fc0c
@heinousjay authored
Showing with 1,285 additions and 729 deletions.
  1. +0 −3  .gitmodules
  2. +1 −1  ...rJabbr-server/{JibbrJabbr-kernel/src/main/java/jj → JibbrJabbr-api/src/main/java/jj/api}/Blocking.java
  3. +47 −0 JibbrJabbr-server/JibbrJabbr-api/src/main/java/jj/api/Event.java
  4. +5 −2 ...bbr-server/{JibbrJabbr-kernel/src/main/java/jj → JibbrJabbr-api/src/main/java/jj/api}/NonBlocking.java
  5. +16 −4 JibbrJabbr-server/JibbrJabbr-bootstrapper/src/main/java/jj/BootstrapClassLoader.java
  6. +8 −0 JibbrJabbr-server/JibbrJabbr-kernel/pom.xml
  7. +46 −1 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/Application.java
  8. +0 −60 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/DaemonKernelController.java
  9. +224 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/EventMediationService.java
  10. +28 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/EventPublisher.java
  11. +202 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/HttpRequestHandler.java
  12. +60 −35 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/HttpServer.java
  13. +48 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJComponentMonitor.java
  14. +15 −28 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJLifecycleStrategy.java
  15. +2 −42 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJThreadPoolExecutor.java
  16. +74 −205 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/Kernel.java
  17. +24 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/KernelControl.java
  18. +6 −20 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/{KernelController.java → KernelException.java}
  19. +4 −1 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/KernelMessages.java
  20. +1 −1  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/LocLoggerProvidingAdapter.java
  21. +11 −165 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/NettyRequestBridge.java
  22. +155 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/generation/EventMediationTransformer.java
  23. +1 −1  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragment.java
  24. +0 −53 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragmentCache.java
  25. +2 −2 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragmentFinder.java
  26. +2 −2 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageBundle.java
  27. +1 −1  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageConveyor.java
  28. +2 −2 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageConveyorCache.java
  29. +2 −2 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/PreloadingMessageConveyor.java
  30. +1 −1  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/logging/LoggingQueue.java
  31. 0  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/{ → builtin}/assets/favicon.ico
  32. 0  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/{ → builtin}/assets/index.html
  33. 0  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/{ → builtin}/errors/404.html
  34. 0  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/{ → builtin}/errors/405.html
  35. +1 −0  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/kernel-messages_en.properties
  36. +4 −3 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/fun/ExampleAdapter.java
  37. +60 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/ApplicationTest.java
  38. +226 −0 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/EventMediationServiceTest.java
  39. +0 −85 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/html/HTMLFragmentCacheTest.java
  40. +0 −5 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/html/HTMLFragmentFinderTest.java
  41. +6 −3 JibbrJabbr-server/pom.xml
  42. +0 −1  jsoup
View
3  .gitmodules
@@ -1,3 +0,0 @@
-[submodule "jsoup"]
- path = jsoup
- url = git://github.com/jhy/jsoup.git
View
2  .../JibbrJabbr-kernel/src/main/java/jj/Blocking.java → ...JibbrJabbr-api/src/main/java/jj/api/Blocking.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package jj;
+package jj.api;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
View
47 JibbrJabbr-server/JibbrJabbr-api/src/main/java/jj/api/Event.java
@@ -0,0 +1,47 @@
+/*
+ * 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.api;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a type as being an event. Instances of that type can automatically participate in
+ * event mediation. Any component with a public void method of any name that takes a single
+ * parameter of an event type will be registered as a listener, and whenever an event is published,
+ * that method will be invoked.
+ *
+ * use the {@link Blocking}/{@link NonBlocking} annotations to indicate if the event listener method might block. if
+ * no annotation is supplied, it will be assumed that blocking is possible. at this point it is not
+ * clear what impacts this will have but it is likely that nonblocking methods will execute more quickly
+ *
+ * static methods will be ignored.
+ *
+ * event objects should almost certainly be immutable. this may be enforced
+ *
+ * @author jason
+ *
+ */
+@Retention(RUNTIME)
+@Target(TYPE)
+@Documented
+@Inherited
+public @interface Event {}
View
7 ...bbrJabbr-kernel/src/main/java/jj/NonBlocking.java → ...brJabbr-api/src/main/java/jj/api/NonBlocking.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package jj;
+package jj.api;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -22,11 +22,14 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+
/**
* <p>
* Documents that a method will never block, and so is suitable for use in
* the asynchronous executor. Methods decorated with this annotation must never
- * call methods decorated with {@link Blocking}.
+ * call methods decorated with {@link Blocking}. Note that all logging is currently
+ * potentially blocking, so there can be no logging from a method decorated
+ * with this annotation.
* </p>
*
* <p>
View
20 JibbrJabbr-server/JibbrJabbr-bootstrapper/src/main/java/jj/BootstrapClassLoader.java
@@ -19,9 +19,14 @@
* @author jason
*
*/
-final class BootstrapClassLoader
+public final class BootstrapClassLoader
extends ClassLoader{
+ public interface ClassEmitter {
+ public String name();
+ public byte[] emit();
+ }
+
private static final String JAR_GLOB = "*.jar";
private static final String CLASS_FILE_FORMAT = "/%s.class";
private static final String RESOURCE_FORMAT = "/%s";
@@ -32,6 +37,10 @@
private final Path libPath;
+ private DirectoryStream<Path> libJarsStream() throws IOException {
+ return Files.newDirectoryStream(libPath, JAR_GLOB);
+ }
+
BootstrapClassLoader(Path libPath) {
// whatever loaded this class is the root of all classloaders in the system
super(BootstrapClassLoader.class.getClassLoader());
@@ -40,9 +49,12 @@
// and we want them enabled
setDefaultAssertionStatus(true);
}
-
- private DirectoryStream<Path> libJarsStream() throws IOException {
- return Files.newDirectoryStream(libPath, JAR_GLOB);
+
+ public Class<?> loadClass(ClassEmitter emitter) {
+ byte[] bytes = emitter.emit();
+ Class<?> result = defineClass(emitter.name(), bytes, 0, bytes.length);
+ resolveClass(result);
+ return result;
}
@Override
View
8 JibbrJabbr-server/JibbrJabbr-kernel/pom.xml
@@ -41,6 +41,14 @@
<artifactId>jcip-annotations</artifactId>
</dependency>
<dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-util</artifactId>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<scope>test</scope>
View
47 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/Application.java
@@ -15,7 +15,17 @@
*/
package jj;
+import java.io.Closeable;
+import java.net.URL;
+
+import jj.api.Blocking;
+import jj.api.NonBlocking;
+
+import net.jcip.annotations.ThreadSafe;
+
/**
+ * Should this be immutable, with new instances created when things change?
+ *
* Represents some level of Application containment,
* coordinating the set of resources that make it up
* - picocontainer
@@ -37,6 +47,41 @@
* @author Jason Miller
*
*/
-public class Application {
+@ThreadSafe
+public class Application implements Closeable {
+
+ protected final URL baseURL;
+ private volatile boolean closed = false;
+ private volatile boolean loaded = false;
+
+ public Application(URL baseURL) throws Exception {
+ assert (baseURL != null) : "baseURL is required";
+
+ this.baseURL = baseURL;
+ load();
+ }
+
+ protected void load() throws Exception {
+ ;
+ }
+
+ @NonBlocking
+ public boolean loaded() {
+ return loaded;
+ }
+
+ @Blocking
+ public boolean respond(String path) {
+ return true;
+ }
+ @Override
+ public void close() {
+ this.closed = true;
+ }
+
+ public boolean closed() {
+ return closed;
+ }
+
}
View
60 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/DaemonKernelController.java
@@ -1,60 +0,0 @@
-/*
- * 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;
-
-/**
- * @author jason
- *
- */
-public class DaemonKernelController implements KernelController {
-
- /* (non-Javadoc)
- * @see jj.KernelController#awaitPrivilegedOperationsComplete()
- */
- @Override
- public void awaitPrivilegedOperationsComplete() {
- // TODO Auto-generated method stub
-
- }
-
- /* (non-Javadoc)
- * @see jj.KernelController#notifyPrivilegedOperationsComplete()
- */
- @Override
- public void notifyPrivilegedOperationsComplete() {
- // TODO Auto-generated method stub
-
- }
-
- /* (non-Javadoc)
- * @see jj.KernelController#awaitHttpServerStart()
- */
- @Override
- public void awaitHttpServerStart() {
- // TODO Auto-generated method stub
-
- }
-
- /* (non-Javadoc)
- * @see jj.KernelController#notifyHttpServerStarted()
- */
- @Override
- public void notifyHttpServerStarted() {
- // TODO Auto-generated method stub
-
- }
-
-}
View
224 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/EventMediationService.java
@@ -0,0 +1,224 @@
+/*
+ * 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.reflect.Modifier.PUBLIC;
+import static java.lang.reflect.Modifier.STATIC;
+import static jj.KernelMessages.*;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.concurrent.LinkedTransferQueue;
+
+import org.slf4j.cal10n.LocLogger;
+
+import ch.qos.cal10n.MessageConveyor;
+
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.ThreadSafe;
+
+import jj.api.Blocking;
+import jj.api.Event;
+import jj.api.NonBlocking;
+
+/**
+ * component with known event
+ *
+ * @author jason
+ *
+ */
+@ThreadSafe
+public class EventMediationService implements EventPublisher {
+
+ @Immutable
+ private static class RegistrationBundle {
+
+ final Class<?> eventType;
+ final Object instance;
+ final Method m;
+ final boolean add;
+ final String key;
+
+ RegistrationBundle(final Class<?> eventType, final Object instance, final Method m, final boolean add) {
+ this.eventType = eventType;
+ this.instance = instance;
+ this.m = m;
+ this.add = add;
+ this.key = new StringBuffer()
+ .append(System.identityHashCode(instance))
+ .append("#")
+ .append(m.getName())
+ .toString();
+ }
+ }
+
+ private final LinkedTransferQueue<RegistrationBundle> registrationQueue = new LinkedTransferQueue<>();
+ private final LinkedTransferQueue<Object> publishQueue = new LinkedTransferQueue<>();
+ private volatile boolean run = true;
+
+ private final LocLogger logger;
+ private final MessageConveyor messages;
+
+ @NonBlocking
+ public EventMediationService(
+ final SynchThreadPool synchPool,
+ final LocLogger logger,
+ final MessageConveyor messages
+ ) {
+ this.logger = logger;
+ this.messages = messages;
+ logger.info(ObjectInstantiated, EventMediationService.class);
+ synchPool.submit(eventLoop);
+ }
+
+ @NonBlocking
+ private void offerToLoop(Object instance, boolean add) {
+ for (Method m: instance.getClass().getMethods()) {
+ Class<?> param;
+ if ((m.getModifiers() & PUBLIC) == PUBLIC &&
+ (m.getModifiers() & STATIC) != STATIC &&
+ m.getReturnType() == Void.TYPE &&
+ m.getParameterTypes().length == 1 &&
+ (param = m.getParameterTypes()[0]).isAnnotationPresent(Event.class)) {
+ registrationQueue.offer(new RegistrationBundle(param, instance, m, add));
+ }
+ }
+ }
+
+ /**
+ * Registers an object instance as an event listener.
+ * @param instance
+ */
+ @NonBlocking
+ public void register(Object instance) {
+ assert (instance != null) : "cannot register a null for events";
+ offerToLoop(instance, true);
+ }
+
+ /**
+ * Unregisters an object instance as an event listener.
+ * @param instance
+ */
+ @NonBlocking
+ public void unregister(Object instance) {
+ assert (instance != null) : "cannot unregister a null for events";
+ offerToLoop(instance, false);
+ }
+
+ /**
+ * Publishes an event
+ * @param event
+ */
+ @NonBlocking
+ public void publish(Object event) {
+ assert (event != null) : "cannot publish a null event";
+ assert (event.getClass().getAnnotation(Event.class) != null) : "cannot publish an object that is not an event";
+ publishQueue.offer(event);
+ }
+
+ /**
+ * Shuts the service down. After this method is called, the instance is useless and
+ * should be discarded.
+ */
+ @NonBlocking
+ public void shutdown() {
+ run = false;
+ }
+
+ // should be weak so things can be collected?
+ // need to investigate that. for now count on
+ // cleanup notifications
+ // can only be accessed from inside the event loop
+ private final HashMap<Class<?>, HashMap<String, RegistrationBundle>> listeners = new HashMap<>();
+
+
+ private final Runnable eventLoop = new Runnable() {
+
+ @Blocking
+ @Override
+ public void run() {
+ Thread.currentThread().setName(messages.getMessage(EventLoopThreadName));
+ try {
+ while (run) {
+ // get the event,
+ // do the registration stuff
+ // publish the event
+ // OVER AND OVER FOREVER
+ // or until shutdown
+
+ Object event = publishQueue.take();
+
+ if (run) { // if we've been shut down, just ignore it all
+ HashMap<String, RegistrationBundle> h;
+ RegistrationBundle i;
+ while ((i = registrationQueue.poll()) != null) {
+
+ if (i.add) {
+
+ //EventMediationTransformer.makeClassBytes(i.eventType, i.instance.getClass(), i.m);
+
+ // use ASM to convert the invocation into a non-reflective
+ // runnable that takes the event as a construction parameter
+ // and calls the method in its run method
+
+ // use the instance class hashcode + '#' + method name as the key into a map of these tasks
+
+ h = listeners.get(i.eventType);
+ if (h == null) {
+ h = new HashMap<>();
+ listeners.put(i.eventType, h);
+ }
+ h.put(i.key, i);
+ } else {
+ h = listeners.get(i.eventType);
+ if (h != null) {
+
+ h.remove(i.key);
+
+ if (h.isEmpty()) {
+ listeners.remove(i.eventType);
+ }
+ }
+ }
+
+ }
+
+ h = listeners.get(event.getClass());
+ if (h != null) {
+ for (RegistrationBundle i2 : h.values()) {
+ try {
+ // instead of direct invocation, should be a runnable that
+ // gets put on the appropriate thread pool
+ i2.m.invoke(i2.instance, event);
+ // what do we do in exception cases? egad
+ } catch (IllegalAccessException e) {
+ // can't happen. if it does we've failed amazingly
+ } catch (InvocationTargetException e) {
+ e.getCause().printStackTrace();
+ }
+ }
+ }
+ }
+
+ }
+ } catch (InterruptedException ie) {
+ run = false;
+ Thread.currentThread().interrupt();
+ }
+ }
+ };
+}
View
28 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/EventPublisher.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ *
+ *
+ * @author jason
+ *
+ */
+public interface EventPublisher {
+
+ public void publish(Object event);
+
+}
View
202 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/HttpRequestHandler.java
@@ -0,0 +1,202 @@
+/*
+ * 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 jj.KernelMessages.ServerErrorFallbackResponse;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
+import static org.jboss.netty.handler.codec.http.HttpMethod.GET;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE;
+import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+
+import jj.api.NonBlocking;
+import jj.html.HTMLFragment;
+import jj.html.HTMLFragmentFinder;
+import net.jcip.annotations.ThreadSafe;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.handler.codec.http.HttpResponse;
+import org.jboss.netty.handler.codec.http.HttpResponseStatus;
+import org.jboss.netty.util.CharsetUtil;
+
+import ch.qos.cal10n.MessageConveyor;
+
+/**
+ * <p>
+ * handles HTTP requests, ostensibly by searching registered handlers for something that care
+ * about this request
+ * </p>
+ *
+ * @author jason
+ *
+ */
+@ThreadSafe
+public class HttpRequestHandler {
+
+ private static final HttpResponse RESPONSE_100_CONTINUE = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
+
+ private final HTMLFragmentFinder htmlFragmentFinder;
+
+ private final byte[] favicon;
+
+ private final HttpResponse error503;
+
+ public HttpRequestHandler(
+ HTMLFragmentFinder htmlFragmentFinder,
+ MessageConveyor messageConveyor
+ ) throws Exception {
+
+ assert htmlFragmentFinder != null : "htmlFragmentFinder is required";
+
+ this.htmlFragmentFinder = htmlFragmentFinder;
+
+ error503 = makeFallback503(messageConveyor.getMessage(ServerErrorFallbackResponse));
+
+ // totally the wrong place for this as well
+ try (InputStream indexStream = HttpRequestHandler.class.getResourceAsStream("builtin/assets/favicon.ico")) {
+ assert indexStream != null;
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024)) {
+ byte[] buffer = new byte[1024];
+ int read = -1;
+ while ((read = indexStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, read);
+ }
+ favicon = outputStream.toByteArray();
+ }
+ }
+ }
+
+ @NonBlocking
+ private HttpResponse makeFallback503(String responseString) {
+ HttpResponse response = new DefaultHttpResponse(HTTP_1_1, SERVICE_UNAVAILABLE);
+ response.setHeader(CONTENT_TYPE, "text/html; charset=" + CharsetUtil.UTF_8.name());
+ response.setContent(
+ ChannelBuffers.wrappedBuffer(
+ responseString.getBytes(CharsetUtil.UTF_8)
+ )
+ );
+
+ return response;
+ }
+
+ @NonBlocking
+ public void handle(final ChannelHandlerContext ctx, final MessageEvent e, final HttpRequest request) throws Exception {
+
+ String path = null;
+ String uri = request.getUri();
+ final Channel responseChannel = e.getChannel();
+ HttpResponseStatus status = NOT_FOUND;
+ if (request.getMethod() != GET) {
+ path = "builtin/errors/405.html";
+ status = METHOD_NOT_ALLOWED;
+ } else {
+ if (is100ContinueExpected(request)) {
+ responseChannel.write(RESPONSE_100_CONTINUE);
+ } else if ("/favicon.ico".equals(uri)) {
+ writeResponse(responseChannel, request, OK, ChannelBuffers.wrappedBuffer(favicon), "image/vnd.microsoft.icon");
+ } else if ("/".equals(uri) || "/index".equals(uri)) {
+ path = "builtin/assets/index.html";
+ status = OK;
+ } else {
+ path = "builtin/errors/404.html";
+ }
+ }
+
+ if (path != null) {
+ final HttpResponseStatus finalStatus = status;
+ try {
+ final FileSystem jarFS = FileSystems.newFileSystem(JJ.jarForClass(HttpRequestHandler.class), null);
+ htmlFragmentFinder.find(jarFS.getPath("jj"), path, new SynchronousOperationCallback<HTMLFragment>() {
+
+ @Override
+ public void complete(HTMLFragment htmlFragment) {
+ writeResponse(responseChannel, request, finalStatus,
+ ChannelBuffers.copiedBuffer(
+ htmlFragment.element().html(),
+ CharsetUtil.UTF_8
+ ),
+ "text/html; charset=UTF-8"
+ );
+ }
+
+ @Override
+ public void throwable(Throwable t) {
+ //logger.error("NOT GOOD", t);
+ write503(responseChannel);
+ }
+ });
+ } catch (Exception ex) {
+ //logger.error("NOT GOOD", ex);
+
+ }
+ }
+ }
+
+ @NonBlocking
+ void write503(Channel channel) {
+ channel.write(error503).addListener(ChannelFutureListener.CLOSE);
+ }
+
+ @NonBlocking
+ private void writeResponse(
+ final Channel responseChannel,
+ final HttpRequest request,
+ final HttpResponseStatus status,
+ final ChannelBuffer content,
+ final String contentType
+ ) {
+ // Decide whether to close the connection or not.
+ boolean keepAlive = isKeepAlive(request);
+
+ // Build the response object.
+ HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
+ response.setContent(content);
+ response.setHeader(CONTENT_TYPE, contentType);
+
+ if (keepAlive) {
+ // Add 'Content-Length' header only for a keep-alive connection.
+ response.setHeader(CONTENT_LENGTH, content.readableBytes());
+ }
+
+ // Write the response.
+ ChannelFuture future = responseChannel.write(response);
+
+ // Close the non-keep-alive connection after the write operation is
+ // done.
+ if (!keepAlive) {
+ future.addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+}
View
95 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/HttpServer.java
@@ -20,8 +20,8 @@
import static jj.KernelMessages.*;
import java.net.InetSocketAddress;
-
-import jj.api.Dispose;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelEvent;
@@ -43,7 +43,7 @@
import org.slf4j.cal10n.LocLogger;
public final class HttpServer {
-
+
private final LocLogger logger;
private final NettyRequestBridge requestHandler;
@@ -52,6 +52,8 @@
private final ServerBootstrap bootstrap;
+ private final CyclicBarrier startBarrier = new CyclicBarrier(2);
+
/** All channels currently in use by the server */
private final ChannelGroup allChannels =
new DefaultChannelGroup(HttpServer.class.getName());
@@ -106,8 +108,7 @@ public HttpServer(
final NettyRequestBridge requestHandler,
final KernelSettings kernelSettings,
final SynchThreadPool bossExecutor,
- final AsyncThreadPool httpExecutor,
- final Kernel.Controller kernelSync
+ final AsyncThreadPool httpExecutor
) {
assert logger != null;
assert requestHandler != null;
@@ -128,41 +129,65 @@ public HttpServer(
)
);
- bootstrap.setPipelineFactory(pipelineFactory);
-
- bootstrap.setParentHandler(new ChannelUpstreamHandler() {
-
- @Override
- public void handleUpstream(final ChannelHandlerContext ctx, final ChannelEvent e) throws Exception {
- if (e instanceof UpstreamChannelStateEvent) {
- UpstreamChannelStateEvent ucse = (UpstreamChannelStateEvent)e;
- if (ucse.getState() == BOUND &&
- ucse.getValue() != null) {
- allChannels.add(e.getChannel());
- logger.info(ReachedStartSyncPoint);
- kernelSync.awaitHttpServerStart();
- logger.info(InterfaceBound, ((UpstreamChannelStateEvent)e).getValue());
- }
- }
- ctx.sendUpstream(e);
- }
- });
+ bossExecutor.submit(initializer);
+
- int port = kernelSettings.port();
- logger.debug(BindingPort, port);
- bootstrap.bind(new InetSocketAddress(port));
}
- @Dispose
- public void dispose() {
- logger.info(HttpServerResourcesReleasing);
- if (!allChannels.close().awaitUninterruptibly(kernelSettings.httpMaxShutdownTimeout(), SECONDS)) {
- logger.warn(ConnectionsRemainPastTimeout, kernelSettings.httpMaxShutdownTimeout());
+ public void control(KernelControl control) {
+
+ switch (control) {
+ case Start:
+ try {
+ startBarrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ // publish the exception as an event. BAM
+ e.printStackTrace();
+ }
+ break;
+
+ case Stop:
+ logger.info(HttpServerResourcesReleasing);
+ if (!allChannels.close().awaitUninterruptibly(kernelSettings.httpMaxShutdownTimeout(), SECONDS)) {
+ logger.warn(ConnectionsRemainPastTimeout, kernelSettings.httpMaxShutdownTimeout());
+ }
+ // TODO kill this after moving the i/o threadpool out
+ bootstrap.releaseExternalResources();
+ logger.info(HttpServerResourcesReleased);
+ break;
}
- // TODO kill this after moving the i/o threadpool out
- bootstrap.releaseExternalResources();
- logger.info(HttpServerResourcesReleased);
+
}
+ private Runnable initializer = new Runnable() {
+
+ @Override
+ public void run() {
+ bootstrap.setPipelineFactory(pipelineFactory);
+
+ bootstrap.setParentHandler(new ChannelUpstreamHandler() {
+
+ @Override
+ public void handleUpstream(final ChannelHandlerContext ctx, final ChannelEvent e) throws Exception {
+ if (e instanceof UpstreamChannelStateEvent) {
+ UpstreamChannelStateEvent ucse = (UpstreamChannelStateEvent)e;
+ if (ucse.getState() == BOUND &&
+ ucse.getValue() != null) {
+ allChannels.add(e.getChannel());
+ logger.info(ReachedStartSyncPoint);
+ startBarrier.await();
+ logger.info(InterfaceBound, ((UpstreamChannelStateEvent)e).getValue());
+ }
+ }
+ ctx.sendUpstream(e);
+ }
+ });
+
+ int port = kernelSettings.port();
+ logger.debug(BindingPort, port);
+ bootstrap.bind(new InetSocketAddress(port));
+ }
+ };
+
}
View
48 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJComponentMonitor.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.monitors.NullComponentMonitor;
+
+/**
+ * @author jason
+ *
+ */
+public class JJComponentMonitor extends NullComponentMonitor {
+
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5754941079119566064L;
+
+
+ /* (non-Javadoc)
+ * @see org.picocontainer.monitors.NullComponentMonitor#instantiated(org.picocontainer.PicoContainer, org.picocontainer.ComponentAdapter, java.lang.reflect.Constructor, java.lang.Object, java.lang.Object[], long)
+ */
+ @Override
+ public <T> void instantiated(PicoContainer container,
+ ComponentAdapter<T> componentAdapter, Constructor<T> constructor,
+ Object instantiated, Object[] injected, long duration) {
+
+ container.getComponent(EventMediationService.class).register(instantiated);
+ }
+
+}
View
43 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJLifecycleStrategy.java
@@ -15,25 +15,13 @@
*/
package jj;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import jj.api.Dispose;
-import jj.api.Shutdown;
-import jj.api.Startup;
-
import org.picocontainer.ComponentAdapter;
import org.picocontainer.LifecycleStrategy;
-import org.picocontainer.PicoLifecycleException;
+import org.picocontainer.PicoContainer;
/**
* <p>
- * Simple mapping from picocontainer lifecycle to JOJ lifecycle.
- * </p>
- *
- * <p>
- * See {@link Startup}, {@link Shutdown}, and {@link Dispose}.
+ * every component has a lifecycle but it's managed elsewhere.
* </p>
*
* @author jason
@@ -41,21 +29,28 @@
*/
class JJLifecycleStrategy implements LifecycleStrategy {
+ private final PicoContainer coreContainer;
+
+ JJLifecycleStrategy(final PicoContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
@Override
public void start(Object component) {
- invoke(component, Startup.class);
+ //invoke(component, Startup.class);
}
@Override
public void stop(Object component) {
- invoke(component, Shutdown.class);
+ //invoke(component, Shutdown.class);
}
@Override
public void dispose(Object component) {
- invoke(component, Dispose.class);
+ //invoke(component, Dispose.class);
+ coreContainer.getComponent(EventMediationService.class).unregister(component);
}
-
+ /*
private void invoke(Object component, Class<? extends Annotation> annotation) {
for (Method method : component.getClass().getMethods()) {
if (method.isAnnotationPresent(annotation)) {
@@ -69,19 +64,11 @@ private void invoke(Object component, Class<? extends Annotation> annotation) {
}
}
}
+ */
@Override
public boolean hasLifecycle(Class<?> type) {
- boolean result = false;
- for (Method method : type.getMethods()) {
- if (method.isAnnotationPresent(Startup.class) ||
- method.isAnnotationPresent(Shutdown.class) ||
- method.isAnnotationPresent(Dispose.class)) {
- result = true;
- break;
- }
- }
- return result;
+ return true;
}
@Override
View
44 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/JJThreadPoolExecutor.java
@@ -15,15 +15,11 @@
*/
package jj;
-import static jj.KernelMessages.*;
-
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import org.slf4j.cal10n.LocLogger;
-
abstract class JJThreadPoolExecutor
extends ThreadPoolExecutor
implements ThreadFactory {
@@ -44,44 +40,8 @@
abstract ThreadGroup threadGroup();
@Override
- protected void beforeExecute(Thread t, Runnable r) {
- //logger.debug(JJTaskStarting, r.getClass());
- super.beforeExecute(t, r);
- }
-
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
-
- if (t != null) {
- //logger.error(JJTaskEndedWithException, r, t);
- } else {
- //logger.debug(JJTaskEnded, r.getClass());
- }
- super.afterExecute(r, t);
- }
-
- @Override
- public Thread newThread(final Runnable inner) {
-
- String name = threadName();
-
-
- //logger.debug(JJThreadInitializing, name, Thread.currentThread().getName());
-
- Thread thread = new Thread(
- threadGroup(),
- new Runnable() {
-
- @Override
- public void run() {
- //logger.trace(JJThreadStarting);
- inner.run();
- //logger.trace(JJThreadExiting);
- }
- },
- name
- );
+ public Thread newThread(final Runnable runnable) {
- return thread;
+ return new Thread(threadGroup(), runnable, threadName());
}
}
View
279 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/Kernel.java
@@ -15,12 +15,10 @@
*/
package jj;
+import static jj.KernelControl.*;
import static org.picocontainer.Characteristics.NONE;
import static org.jboss.netty.util.ThreadNameDeterminer.CURRENT;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-
import jj.api.Version;
import jj.html.HTMLFragmentFinder;
@@ -31,6 +29,7 @@
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.behaviors.Caching;
import org.picocontainer.injectors.ConstructorInjection;
+import org.picocontainer.lifecycle.NullLifecycleStrategy;
import org.picocontainer.monitors.NullComponentMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +37,25 @@
/**
* Puts the server components together and manages their lifecycle.
*
+ * container hierarchy should be something like
+ *
+ * core
+ * - thread pools
+ * - event mediator
+ * - settings
+ * - messaging
+ * - logging
+ * - i/o container
+ * - http/websocket/spdy? handler (netty)
+ * - file watch service
+ * - filesystem service
+ * - application container - uses events to communicate with i/o container, no direct dependencies allowed!
+ * - application loader
+ * - individual application containers?
+ * - responders
+ *
+ * each layer uses the services of the layer(s) underneath
+ *
* @author jason
*
*/
@@ -58,230 +76,81 @@
private final Logger logger = LoggerFactory.getLogger(Kernel.class);
/**
- * Injected into kernel objects so they can be controlled in arbitrary
- * fashion by the kernel object.
- *
- * This design is only vaguely testable, which is worrisome.
- * @author Jason Miller
- *
+ * The core PicoContainer used to bootstrap the event mediator and
+ * server executors
*/
- final class Controller {
-
- private final boolean daemon;
-
- private Controller(final boolean daemon) {
- this.daemon = daemon;
- }
-
- // synchronizing server start
- // sequence is
- // - start HttpServer initialization thread (Kernel)
- // - awaitHttpSocketBound
- // - bind socket (HttpServer)
- // - awaitHttpServerStart
- // at this point, drop privileges in the outside daemon code
- // - notifyHttpServerStarted
- private final ReentrantLock serverStartGate = new ReentrantLock();
- private final Condition socketBound = serverStartGate.newCondition();
- private final Condition serverStarted = serverStartGate.newCondition();
- private volatile boolean socketBoundFlag = false;
- private volatile boolean serverStartedFlag = false;
- private void awaitHttpSocketBound() {
- if (daemon) {
- serverStartGate.lock();
- try {
- while (!socketBoundFlag) {
- logger.info("awaiting socket bound");
- socketBound.awaitUninterruptibly();
- }
- logger.info("socket bound notification delivered");
- } finally {
- serverStartGate.unlock();
- }
- }
- }
- void awaitHttpServerStart() {
- if (daemon) {
- serverStartGate.lock();
- try {
- socketBoundFlag = true;
- logger.info("notifying socket bound");
- socketBound.signal();
- while (!serverStartedFlag) {
- logger.info("awaiting server start");
- serverStarted.awaitUninterruptibly();
- }
- logger.info("server start notification delivered");
- } finally {
- serverStartGate.unlock();
- }
- }
- }
- private void notifyHttpServerStarted() {
- if (daemon) {
- serverStartGate.lock();
- try {
- serverStartedFlag = true;
- logger.info("notifying server started");
- serverStarted.signal();
- } finally {
- serverStartGate.unlock();
- }
- }
- }
-
- private volatile boolean clearToServe = false;
- public boolean clearToServe() {
- return clearToServe;
- }
- }
+ private final MutablePicoContainer coreContainer =
+ new DefaultPicoContainer(
+ new Caching().wrap(new ConstructorInjection()),
+ new NullLifecycleStrategy(),
+ null,
+ new NullComponentMonitor()
+ );
/**
- * Defines how the kernel manages its lifecycle,
- * depending on how it was invoked
- * @author Jason Miller
- *
+ * The PicoContainer used to talk to the outside world. communicates with
+ * the rest of the system entirely via events
*/
- private interface KernelLifecycleStrategy {
-
- void init();
- void start();
- void stop();
- void dispose();
- }
+ private final MutablePicoContainer ioContainer =
+ new DefaultPicoContainer(
+ new Caching().wrap(new ConstructorInjection()),
+ new JJLifecycleStrategy(coreContainer),
+ coreContainer,
+ new JJComponentMonitor()
+ );
- /**
- * Kernel lifecycle when we are our own process
- * @author Jason Miller
- *
- */
- private final class ProcessLifecycle
- implements KernelLifecycleStrategy {
+ private final EventMediationService ems;
+
+ public Kernel(String[] args, boolean daemon) {
- @Override
- public void init() {
- logger.info("Process.init");
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- // this is to be split up later, when there is a way
- // of externally controlling the kernel lifecycle.
- // for now it can just stay in here
- ProcessLifecycle.this.stop();
- ProcessLifecycle.this.dispose();
- }
- });
- coreContainer.start();
- logger.info("Process.init complete");
- }
-
- @Override
- public void start() {
- sync.clearToServe = true;
- }
-
- @Override
- public void stop() {
- sync.clearToServe = false;
- }
-
- @Override
- public void dispose() {
- coreContainer.dispose();
- }
+ // move this into something else
+ logger.info("Welcome to {} {}", Version.name, Version.version);
- }
+ coreContainer.setName("Kernel Core");
+
+ coreContainer.addComponent(args)
+ .addComponent(KernelSettings.class)
+ .addComponent(EventMediationService.class)
+ .addComponent(SynchronousThreadPoolExecutor.class)
+ .addComponent(AsynchronousThreadPoolExecutor.class)
+ .addAdapter(new MessageConveyorProvidingAdapter())
+ .as(NONE).addAdapter(new LocLoggerProvidingAdapter());
+
+ coreContainer.addChildContainer(ioContainer);
+
+ ioContainer.setName("Kernel I/O");
- private final class DaemonLifecycle
- implements KernelLifecycleStrategy {
+ ioContainer.addComponent(HTMLFragmentFinder.class)
+ .addComponent(HttpRequestHandler.class)
+ .addComponent(HttpServer.class)
+ .addComponent(NettyRequestBridge.class);
- @Override
- public void init() {
- logger.info("Daemon.init");
-
- new Thread("kernel initialization helper") {
+ coreContainer.start();
+
+ ems = coreContainer.getComponent(EventMediationService.class);
+
+ if (!daemon) {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
- // TODO Auto-generated method stub
- // ugly mess of a thing, this is...
- // gonna have to extract the lifecycle stuff,
- // picocontainer keeps making me sad.
- coreContainer.start();
+ Kernel.this.stop();
+ Kernel.this.dispose();
}
- }.start();
+ });
- sync.awaitHttpSocketBound();
- logger.info("Daemon.init complete");
+ start();
}
-
- @Override
- public void start() {
- sync.notifyHttpServerStarted();
- sync.clearToServe = true;
- }
-
- @Override
- public void stop() {
- sync.clearToServe = false;
- }
-
- @Override
- public void dispose() {
- coreContainer.dispose();
- }
-
- }
-
-
- private final Controller sync;
-
- private final KernelLifecycleStrategy lifecycle;
-
- /**
- * The core PicoContainer used to hold the most basic server
- * objects.
- */
- private final MutablePicoContainer coreContainer =
- new DefaultPicoContainer(
- new Caching().wrap(new ConstructorInjection()),
- new JJLifecycleStrategy(),
- null,
- new NullComponentMonitor()
- );
-
- public Kernel(String[] args, boolean daemon) {
-
- logger.info("Welcome to {} {}", Version.name, Version.version);
-
- sync = new Controller(daemon);
-
- lifecycle = daemon ? new DaemonLifecycle() : new ProcessLifecycle();
-
- coreContainer.setName("Kernel");
-
- coreContainer.addComponent(sync)
- .addComponent(args)
- .addComponent(KernelSettings.class)
- .addComponent(HTMLFragmentFinder.class)
- .addComponent(HttpServer.class)
- .addComponent(NettyRequestBridge.class)
- .addComponent(SynchronousThreadPoolExecutor.class)
- .addComponent(AsynchronousThreadPoolExecutor.class)
- .addAdapter(new MessageConveyorProvidingAdapter())
- .as(NONE).addAdapter(new LocLoggerProvidingAdapter());
-
- lifecycle.init();
}
public void start() {
- lifecycle.start();
+ ems.publish(Start);
}
public void stop() {
- lifecycle.stop();
+ ems.publish(Stop);
}
public void dispose() {
- lifecycle.dispose();
+
}
}
View
24 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/KernelControl.java
@@ -0,0 +1,24 @@
+/*
+ * 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 jj.api.Event;
+
+@Event
+enum KernelControl {
+ Start,
+ Stop
+}
View
26 ...bbr-kernel/src/main/java/jj/KernelController.java → ...abbr-kernel/src/main/java/jj/KernelException.java
@@ -15,32 +15,18 @@
*/
package jj;
+import jj.api.Event;
+
/**
- * TODO needs a better name
- *
- * <p>
- * Provides encapsulated command and control services for kernel systems
- * </p>
- *
* @author jason
*
*/
-interface KernelController {
+@Event
+public class KernelException extends RuntimeException {
/**
- * waiting point for the initialization thread to
- * pause until all privileged operations are complete.
- * This is mainly to support binding to well-known ports
- * on Unix - this will probably have to change?
- */
- void awaitPrivilegedOperationsComplete();
-
- /**
*
*/
- void notifyPrivilegedOperationsComplete();
-
- void awaitHttpServerStart();
-
- void notifyHttpServerStarted();
+ private static final long serialVersionUID = -3110191078023953752L;
+
}
View
5 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/KernelMessages.java
@@ -68,5 +68,8 @@
// AsynchronousThreadPoolExecutor messages
AsynchronousTaskRejected,
AsynchronousTaskDone,
- AsynchronousThreadName
+ AsynchronousThreadName,
+
+ // EventMediationService messages
+ EventLoopThreadName
}
View
2  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/LocLoggerProvidingAdapter.java
@@ -59,7 +59,7 @@ public LocLogger getComponentInstance(PicoContainer container, Type into)
if (into == null ||
into == ComponentAdapter.NOTHING.class) {
logger.warn(UsingUnknownLogger);
- factory.getLocLogger(UNKNOWN_LOGGER_NAME);
+ return factory.getLocLogger(UNKNOWN_LOGGER_NAME);
}
String loggerType = into.toString();
View
176 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/NettyRequestBridge.java
@@ -16,36 +16,14 @@
package jj;
import static jj.KernelMessages.*;
-import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
-import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
-import static org.jboss.netty.handler.codec.http.HttpMethod.GET;
-import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
-import static org.jboss.netty.handler.codec.http.HttpVersion.*;
+import jj.api.NonBlocking;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
-
-import jj.html.HTMLFragment;
-import jj.html.HTMLFragmentFinder;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelFuture;
-import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
@@ -53,11 +31,8 @@
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
-import org.jboss.netty.util.CharsetUtil;
import org.slf4j.cal10n.LocLogger;
-import ch.qos.cal10n.MessageConveyor;
-
/**
* Pulls the
* @author Jason Miller
@@ -67,59 +42,21 @@
private final LocLogger logger;
- private final Kernel.Controller controller;
-
- private final HTMLFragmentFinder htmlFragmentFinder;
-
- private final byte[] favicon;
-
- private final HttpResponse error503;
+ private final HttpRequestHandler httpRequestHandler;
public NettyRequestBridge(
- final MessageConveyor messageConveyor,
final LocLogger logger,
- final Kernel.Controller controller,
- final HTMLFragmentFinder htmlFragmentFinder
+ final HttpRequestHandler httpRequestHandler
) throws Exception {
- assert messageConveyor != null : "messageConveyor is required";
assert logger != null : "logger is required";
- assert controller != null : "controller is required";
- assert htmlFragmentFinder != null : "htmlFragmentFinder is required";
+ assert (httpRequestHandler != null) : "httpRequestHandler is required";
logger.debug(ObjectInstantiating, NettyRequestBridge.class);
this.logger = logger;
- this.controller = controller;
- this.htmlFragmentFinder = htmlFragmentFinder;
-
- error503 = makeFallback503(messageConveyor.getMessage(ServerErrorFallbackResponse));
+ this.httpRequestHandler = httpRequestHandler;
- // totally the wrong place for this as well
- try (InputStream indexStream = NettyRequestBridge.class.getResourceAsStream("assets/favicon.ico")) {
- assert indexStream != null;
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024)) {
- byte[] buffer = new byte[1024];
- int read = -1;
- while ((read = indexStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, read);
- }
- favicon = outputStream.toByteArray();
- }
- }
- }
-
- @NonBlocking
- private HttpResponse makeFallback503(String responseString) {
- HttpResponse response = new DefaultHttpResponse(HTTP_1_1, SERVICE_UNAVAILABLE);
- response.setHeader(CONTENT_TYPE, "text/html; charset=" + CharsetUtil.UTF_8.name());
- response.setContent(
- ChannelBuffers.wrappedBuffer(
- responseString.getBytes(CharsetUtil.UTF_8)
- )
- );
-
- return response;
}
@Override
@@ -127,7 +64,7 @@ private HttpResponse makeFallback503(String responseString) {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
// if we're paused, return a 503 directly
- if (controller.clearToServe()) {
+ //if (controller.clearToServe()) {
// if it's a websocket request, adjust the pipeline, then... i dunno
// otherwise make a kernel task to process it
Object msg = e.getMessage();
@@ -145,107 +82,17 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex
ctx.setAttachment(handshaker);
}
} else {
- handleHttp(ctx, e, request);
+ httpRequestHandler.handle(ctx, e, request);
}
} else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
}
- } else {
- write503(e.getChannel());
- }
- }
-
- private void write503(Channel channel) {
- channel.write(error503)
- .addListener(ChannelFutureListener.CLOSE);
+ //} else {
+ // httpRequestHandler.write503(e.getChannel());
+ //}
}
- private static final HttpResponse RESPONSE_100_CONTINUE = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
- @NonBlocking
- private void handleHttp(final ChannelHandlerContext ctx, final MessageEvent e, final HttpRequest request) throws Exception {
- // this totally does not belong here.
- // need to set up a system of handlers
- String path = null;
- String uri = request.getUri();
- final Channel responseChannel = e.getChannel();
- HttpResponseStatus status = NOT_FOUND;
- if (request.getMethod() != GET) {
- path = "errors/405.html";
- status = METHOD_NOT_ALLOWED;
- } else {
- if (is100ContinueExpected(request)) {
- responseChannel.write(RESPONSE_100_CONTINUE);
- } else if ("/favicon.ico".equals(uri)) {
- writeResponse(responseChannel, request, OK, ChannelBuffers.wrappedBuffer(favicon), "image/vnd.microsoft.icon");
- } else if ("/".equals(uri) || "/index".equals(uri)) {
- path = "assets/index.html";
- status = OK;
- } else {
- path = "errors/404.html";
- }
- }
-
- if (path != null) {
- final HttpResponseStatus finalStatus = status;
- try {
- FileSystem jarFS = FileSystems.newFileSystem(JJ.jarForClass(NettyRequestBridge.class), null);
- htmlFragmentFinder.find(jarFS.getPath("jj"), path, new SynchronousOperationCallback<HTMLFragment>() {
-
- @Override
- public void complete(HTMLFragment htmlFragment) {
- writeResponse(responseChannel, request, finalStatus,
- ChannelBuffers.copiedBuffer(
- htmlFragment.element().html(),
- CharsetUtil.UTF_8
- ),
- "text/html; charset=UTF-8"
- );
- }
-
- @Override
- public void throwable(Throwable t) {
- logger.error("NOT GOOD", t);
- write503(responseChannel);
- }
- });
- } catch (Exception ex) {
- logger.error("NOT GOOD", ex);
-
- }
- }
- }
-
- @NonBlocking
- private void writeResponse(
- final Channel responseChannel,
- final HttpRequest request,
- final HttpResponseStatus status,
- final ChannelBuffer content,
- final String contentType
- ) {
- // Decide whether to close the connection or not.
- boolean keepAlive = isKeepAlive(request);
-
- // Build the response object.
- HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
- response.setContent(content);
- response.setHeader(CONTENT_TYPE, contentType);
-
- if (keepAlive) {
- // Add 'Content-Length' header only for a keep-alive connection.
- response.setHeader(CONTENT_LENGTH, content.readableBytes());
- }
-
- // Write the response.
- ChannelFuture future = responseChannel.write(response);
-
- // Close the non-keep-alive connection after the write operation is
- // done.
- if (!keepAlive) {
- future.addListener(ChannelFutureListener.CLOSE);
- }
- }
private static final String WEBSOCKET_URI = "/websocket";
@@ -275,8 +122,7 @@ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame fram
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
- // do something ridiculous
- // logging as an error is probably not the right thing
+ // publish the exception event
logger.warn("Exception during I/O, dropping the channel", e.getCause());
e.getChannel().close();
}
View
155 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/generation/EventMediationTransformer.java
@@ -0,0 +1,155 @@
+/*
+ * 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.generation;
+
+import java.lang.reflect.Method;
+
+import jj.BootstrapClassLoader.ClassEmitter;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * @author jason
+ *
+ */
+public class EventMediationTransformer implements Opcodes, ClassEmitter {
+
+ private static final String EVENT = "event";
+ private static final String INSTANCE = "instance";
+
+ private final Class<?> eventClass;
+ private final Class<?> listenerClass;
+ private final Method listener;
+
+ public EventMediationTransformer(
+ final Class<?> eventClass,
+ final Class<?> listenerClass,
+ final Method listener
+ ) {
+ this.eventClass = eventClass;
+ this.listenerClass = listenerClass;
+ this.listener = listener;
+ }
+
+ public interface EventRunnable extends Runnable {
+ public void set(final Object instance, final Object event);
+ }
+
+ public String name() {
+ return "jj.EventMediationService$" + listenerClass.getSimpleName() + "EventRunnable";
+ }
+
+
+ public byte[] emit() {
+
+ String name = "jj/EventMediationService$" + listenerClass.getSimpleName() + "EventRunnable";
+
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+
+ cw.visit(
+ V1_7,
+ ACC_PUBLIC + ACC_SUPER,
+ name,
+ null,
+ "java/lang/Object",
+ new String[] { Type.getInternalName(EventRunnable.class) });
+
+
+ {
+ fv = cw.visitField(ACC_PRIVATE, INSTANCE,
+ Type.getDescriptor(listenerClass),
+ null, null);
+ fv.visitEnd();
+ }
+
+ {
+ fv = cw.visitField(ACC_PRIVATE, EVENT,
+ Type.getDescriptor(eventClass), null,
+ null);
+ fv.visitEnd();
+ }
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>",
+ "()V");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "set",
+ "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitTypeInsn(CHECKCAST, Type.getInternalName(listenerClass));
+ mv.visitFieldInsn(
+ PUTFIELD,
+ name,
+ INSTANCE,
+ Type.getDescriptor(listenerClass));
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, Type.getInternalName(eventClass));
+ mv.visitFieldInsn(
+ PUTFIELD,
+ name,
+ EVENT,
+ Type.getDescriptor(eventClass));
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 3);
+ mv.visitEnd();
+ }
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(
+ GETFIELD,
+ name,
+ INSTANCE,
+ Type.getDescriptor(listenerClass));
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(
+ GETFIELD,
+ name,
+ EVENT,
+ Type.getDescriptor(eventClass));
+ mv.visitMethodInsn(INVOKEVIRTUAL,
+ Type.getInternalName(listenerClass),
+ listener.getName(),
+ "("+ Type.getDescriptor(eventClass) +")V");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 1);
+ mv.visitEnd();
+ }
+
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
View
2  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragment.java
@@ -19,7 +19,7 @@
import java.util.List;
import java.util.regex.Pattern;
-import jj.NonBlocking;
+import jj.api.NonBlocking;
import net.jcip.annotations.Immutable;
View
53 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragmentCache.java
@@ -1,53 +0,0 @@
-package jj.html;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.concurrent.ConcurrentHashMap;
-
-import net.jcip.annotations.ThreadSafe;
-
-import org.jboss.netty.util.CharsetUtil;
-
-@ThreadSafe
-public class HTMLFragmentCache {
-
- private final ConcurrentHashMap<Path, HTMLFragment> cache;
-
- public HTMLFragmentCache() {
- cache = new ConcurrentHashMap<>();
- }
-
- /**
- * Returns an HTMLFragment found at a particular location
- * identified by a base Path and a url path segment
- * @param base
- * @param url
- * @return
- */
- public HTMLFragment find(Path base, String url) {
- if (base == null) throw new IllegalArgumentException("");
- if (url == null) throw new IllegalArgumentException("");
- return find(base.resolve(url));
- }
-
- public HTMLFragment find(Path location) {
- if (location == null) throw new IllegalArgumentException("");
- if (!cache.containsKey(location)) {
- HTMLFragment htmlFragment = constructFragment(location);
- if (htmlFragment != null) {
- cache.putIfAbsent(location, htmlFragment);
- }
- }
- return cache.get(location);
-
- }
-
- private HTMLFragment constructFragment(Path location) {
- try {
- return new HTMLFragment(new String(Files.readAllBytes(location), CharsetUtil.UTF_8));
- } catch (IOException e) {
- return null;
- }
- }
-}
View
4 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/html/HTMLFragmentFinder.java
@@ -6,10 +6,10 @@
import java.util.concurrent.ConcurrentHashMap;
import jj.AsyncThreadPool;
-import jj.Blocking;
-import jj.NonBlocking;
import jj.SynchronousOperationCallback;
import jj.SynchThreadPool;
+import jj.api.Blocking;
+import jj.api.NonBlocking;
import net.jcip.annotations.ThreadSafe;
View
4 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageBundle.java
@@ -28,8 +28,8 @@
import java.util.Properties;
import java.util.Set;
-import jj.Blocking;
-import jj.NonBlocking;
+import jj.api.Blocking;
+import jj.api.NonBlocking;
import net.jcip.annotations.Immutable;
View
2  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageConveyor.java
@@ -15,7 +15,7 @@
*/
package jj.resource;
-import jj.NonBlocking;
+import jj.api.NonBlocking;
/**
View
4 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/MessageConveyorCache.java
@@ -19,10 +19,10 @@
import java.util.concurrent.ConcurrentHashMap;
import jj.AsyncThreadPool;
-import jj.Blocking;
-import jj.NonBlocking;
import jj.SynchThreadPool;
import jj.SynchronousOperationCallback;
+import jj.api.Blocking;
+import jj.api.NonBlocking;
import net.jcip.annotations.ThreadSafe;
/**
View
4 JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/jj/resource/PreloadingMessageConveyor.java
@@ -20,8 +20,8 @@
import net.jcip.annotations.Immutable;
-import jj.Blocking;
-import jj.NonBlocking;
+import jj.api.Blocking;
+import jj.api.NonBlocking;
/**
* Custom implementation of the IMessageConveyor interface from cal10n,
View
2  JibbrJabbr-server/JibbrJabbr-kernel/src/main/java/logging/LoggingQueue.java
@@ -21,8 +21,8 @@
import net.jcip.annotations.ThreadSafe;
-import jj.NonBlocking;
import jj.SynchThreadPool;
+import jj.api.NonBlocking;
/**
* Simple backbone for non-blocking logging. Dump messages on the queue and they
View
0  ...r-kernel/src/main/resources/jj/assets/favicon.ico → .../src/main/resources/jj/builtin/assets/favicon.ico
File renamed without changes
View
0  ...br-kernel/src/main/resources/jj/assets/index.html → ...l/src/main/resources/jj/builtin/assets/index.html
File renamed without changes
View
0  ...abbr-kernel/src/main/resources/jj/errors/404.html → ...nel/src/main/resources/jj/builtin/errors/404.html
File renamed without changes
View
0  ...abbr-kernel/src/main/resources/jj/errors/405.html → ...nel/src/main/resources/jj/builtin/errors/405.html
File renamed without changes
View
1  JibbrJabbr-server/JibbrJabbr-kernel/src/main/resources/jj/kernel-messages_en.properties
@@ -24,3 +24,4 @@ JJThreadStarting=Thread starting
JJThreadExiting=Thread ending
JJTaskEnded=Completed task [{0}]
JJTaskEndedWithException=Task [{0}] ended with exception
+EventLoopThreadName=Main Event Loop
View
7 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/fun/ExampleAdapter.java
@@ -1,6 +1,7 @@
package fun;
-import org.jsoup.nodes.Element;
+import jj.api.html.Element;
+
import org.slf4j.Logger;
// this should probably descend from a base class with
@@ -38,10 +39,10 @@ public Element execute() {
// element sets you need and does the manipulations internally if
// it comes to that)
logger.trace("Calling displayTime");
- //adapted.displayTime(element.select("#currentDate").first());
+ adapted.displayTime(element.select("#currentDate").first());
logger.trace("Calling cleanParagraphs");
- //adapted.cleanParagraphs(element.select("p"));
+ adapted.cleanParagraphs(element.select("p"));
return element;
}
View
60 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/ApplicationTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URL;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * @author jason
+ *
+ */
+public class ApplicationTest {
+
+ private final URL clamwhoresURL = ApplicationTest.class.getResource("");
+
+ private Application clamwhores() throws Exception {
+ return new Application(clamwhoresURL);
+ }
+
+ @Test @Ignore
+ public void testLifecycle() throws Exception {
+ Application clamwhores = clamwhores();
+ assertThat(clamwhores, is(notNullValue()));
+ assertTrue("clamwhores did not indicate a successful load", clamwhores.loaded());
+ assertFalse("clamwhores indicated closed when should be not closed", clamwhores.closed());
+ clamwhores.close();
+ assertTrue("clamwhores indicated not closed when should be closed", clamwhores.closed());
+ }
+
+ @Test @Ignore
+ public void testApplicationRespondsToExpectedURLs() throws Exception {
+ Application clamwhores = clamwhores();
+ assertTrue("clamwhores should respond to /index", clamwhores.respond("/index"));
+ assertTrue("clamwhores should respond to /style.css", clamwhores.respond("/style.css"));
+ assertTrue("clamwhores should respond to /clamwhores.com.png", clamwhores.respond("/clamwhores.com.png"));
+ assertTrue("clamwhores should respond to /fragment", clamwhores.respond("/fragment"));
+ assertFalse("clamwhores should not respond to /random", clamwhores.respond("/random"));
+ assertFalse("clamwhores should not respond to /paths", clamwhores.respond("/paths"));
+ assertFalse("clamwhores should not respond to /do", clamwhores.respond("/do"));
+ assertFalse("clamwhores should not respond to /notwork", clamwhores.respond("/notwork"));
+ }
+}
View
226 JibbrJabbr-server/JibbrJabbr-kernel/src/test/java/jj/EventMediationServiceTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.fail;
+
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jj.api.Event;
+import jj.api.NonBlocking;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.cal10n.LocLogger;
+
+import ch.qos.cal10n.MessageConveyor;
+
+/**
+ * @author jason
+ *
+ */
+public class EventMediationServiceTest {
+
+ /**
+ * Tunable delay to allow the thread to do
+ * its work. no real way to wait for it.
+ */
+ private static final int DELAY = 10;
+
+ @Event
+ public enum TestEvent1 {
+ TYPE_1,
+ TYPE_2
+ }
+
+ @Event
+ public class TestEvent2 {
+ final int value;
+
+ TestEvent2(final int value) {
+ this.value = value;
+ }
+ }
+
+ TestThreadPool ttp;
+ EventMediationService ems;
+
+ AtomicInteger count;
+ AtomicBoolean registered;
+ StringBuffer failedMessages;
+ AtomicReference<CountDownLatch> cdl;
+
+ class Sink1 {
+ @NonBlocking
+ public void wheee(TestEvent1 event) {
+ count.incrementAndGet();
+ if (registered.get()) {
+ cdl.get().countDown();
+ } else {
+ failedMessages.append("wheee called when not registered\n");
+ }