Skip to content

Commit

Permalink
Listener factory configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
Hippo committed Mar 18, 2021
1 parent 3846f7c commit 1919ca8
Show file tree
Hide file tree
Showing 20 changed files with 695 additions and 113 deletions.
4 changes: 1 addition & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ plugins {
}

group 'rip.hippo'
version '5.1.2'
version '5.2.0'

sourceCompatibility = 1.8

Expand All @@ -30,8 +30,6 @@ repositories {

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.ow2.asm', name: 'asm', version: '9.0'
compile group: 'org.ow2.asm', name: 'asm-tree', version: '9.0'
}

publishing {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/rip/hippo/lwjeb/annotation/Filter.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@
*
* @return The filters.
*/
Class<? extends MessageFilter>[] value();
Class<? extends MessageFilter<?>>[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,16 @@


import rip.hippo.lwjeb.configuration.config.Configuration;
import rip.hippo.lwjeb.configuration.config.impl.*;
import rip.hippo.lwjeb.configuration.exception.BusConfigurationException;
import rip.hippo.lwjeb.configuration.config.impl.AsynchronousPublicationConfiguration;
import rip.hippo.lwjeb.configuration.config.impl.BusConfiguration;
import rip.hippo.lwjeb.configuration.config.impl.BusPubSubConfiguration;
import rip.hippo.lwjeb.configuration.config.impl.ExceptionHandlingConfiguration;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
* @author Hippo
* @version 5.0.0, 10/26/19
* @version 5.0.1, 10/26/19
* @since 5.0.0
*
* The bus configurations hold all the configuration instances.
Expand Down Expand Up @@ -62,6 +59,7 @@ public static BusConfigurations getDefault() {
configurations.configurationMap.put(BusConfiguration.class, BusConfiguration.getDefault());
configurations.configurationMap.put(ExceptionHandlingConfiguration.class, ExceptionHandlingConfiguration.getDefault());
configurations.configurationMap.put(BusPubSubConfiguration.class, BusPubSubConfiguration.getDefault());
configurations.configurationMap.put(ListenerFactoryConfiguration.class, ListenerFactoryConfiguration.getDefault());
return configurations;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* A configuration helps configure the bus to your exact likings.
*/
@FunctionalInterface
public interface Configuration<T extends Configuration> {
public interface Configuration<T extends Configuration<?>> {

/**
* Gets the default configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ public static AsynchronousPublicationConfiguration getDefault() {
*/
@Override
public AsynchronousPublicationConfiguration provideDefault() {
AsynchronousPublicationConfiguration configuration = new AsynchronousPublicationConfiguration();
configuration.setDispatcherCount(3);
configuration.setSuppressDispatcherInterrupt(true);
configuration.setDaemonThreads(false);
return configuration;
this.setDispatcherCount(3);
this.setSuppressDispatcherInterrupt(true);
this.setDaemonThreads(false);
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ public static BusConfiguration getDefault() {
*/
@Override
public BusConfiguration provideDefault() {
BusConfiguration configuration = new BusConfiguration();
configuration.setIdentifier("LWJEB");
configuration.setListenerClassLoader(new ListenerClassLoader(this.getClass().getClassLoader()));
return configuration;
this.setIdentifier("LWJEB");
this.setListenerClassLoader(new ListenerClassLoader(this.getClass().getClassLoader()));
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ public static BusPubSubConfiguration getDefault() {
*/
@Override
public BusPubSubConfiguration provideDefault() {
BusPubSubConfiguration configuration = new BusPubSubConfiguration();
configuration.setSubscriber(new WeakReferencedListenerSubscriber<>());
configuration.setPublisher(new StandardMessagePublisher<>());
configuration.setScanner(new MethodBasedMessageScanner<>());
return configuration;
this.setSubscriber(new WeakReferencedListenerSubscriber<>());
this.setPublisher(new StandardMessagePublisher<>());
this.setScanner(new MethodBasedMessageScanner<>());
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ public static ExceptionHandlingConfiguration getDefault() {
*/
@Override
public ExceptionHandlingConfiguration provideDefault() {
ExceptionHandlingConfiguration configuration = new ExceptionHandlingConfiguration();
configuration.setExceptionHandler(StandardExceptionHandler.INSTANCE);
return configuration;
this.setExceptionHandler(StandardExceptionHandler.INSTANCE);
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package rip.hippo.lwjeb.configuration.config.impl;

import rip.hippo.lwjeb.configuration.config.Configuration;
import rip.hippo.lwjeb.configuration.invocation.ListenerFactory;
import rip.hippo.lwjeb.configuration.invocation.impl.DirectListenerFactory;


/**
* @author Hippo
* @version 1.0.0, 3/18/21
* @since 5.2.0
*
* The listener factory configuration allows you to configure how your listeners are created.
*/
public final class ListenerFactoryConfiguration implements Configuration<ListenerFactoryConfiguration> {

/**
* The listener factory.
*/
private ListenerFactory listenerFactory;

/**
* Static constructor wrapper to create the default config.
*
* @return The default config.
*/
public static ListenerFactoryConfiguration getDefault() {
return new ListenerFactoryConfiguration().provideDefault();
}

/**
* Sets the default config values.
*
* @return The default config.
*/
@Override
public ListenerFactoryConfiguration provideDefault() {
this.setListenerFactory(new DirectListenerFactory());
return this;
}

/**
* Gets the listener factory.
*
* @return The listener factory.
*/
public ListenerFactory getListenerFactory() {
return listenerFactory;
}

/**
* Sets the listener factory.
*
* @param listenerFactory The new listener factory.
*/
public void setListenerFactory(ListenerFactory listenerFactory) {
this.listenerFactory = listenerFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package rip.hippo.lwjeb.configuration.invocation;

import rip.hippo.lwjeb.configuration.config.impl.BusConfiguration;
import rip.hippo.lwjeb.configuration.config.impl.ExceptionHandlingConfiguration;
import rip.hippo.lwjeb.listener.Listener;

import java.lang.reflect.Method;

/**
* @author Hippo
* @version 1.0.0, 3/17/21
* @since 5.2.0
*
* A Listener factory is a functional interface on which job is to create {@link Listener}s.
*/
@FunctionalInterface
public interface ListenerFactory {

/**
* Creates a listener.
*
* @param parent The parent method.
* @param method The method to invoke.
* @param topic The event topic.
* @param config The bus configuration.
* @return The listener.
*/
Listener create(Class<?> parent, Method method, Class<?> topic, BusConfiguration config);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package rip.hippo.lwjeb.configuration.invocation.impl;

import rip.hippo.lwjeb.configuration.config.impl.BusConfiguration;
import rip.hippo.lwjeb.configuration.config.impl.ExceptionHandlingConfiguration;
import rip.hippo.lwjeb.configuration.invocation.ListenerFactory;
import rip.hippo.lwjeb.listener.Listener;
import rip.hippo.lwjeb.listener.classfile.ListenerClassFile;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
* @author Hippo
* @version 1.0.0, 3/17/21
* @since 5.2.0
*
* A direct listener factory will dynamically create a listener proxy class to invoke its message handlers directly.
*/
public final class DirectListenerFactory implements ListenerFactory {

/**
* Dynamically generates a listener to invoke {@code method}.
*
* @param parent The parent.
* @param method The method.
* @param topic The topic.
* @param config The bus configuration.
* @return The dynamic listener.
*/
@Override
public Listener create(Class<?> parent, Method method, Class<?> topic, BusConfiguration config) {
String name = "lwjeb/generated/" + parent.getName().replace('.', '/') + "/" + getUniqueMethodName(method);
ListenerClassFile listenerClassFile = new ListenerClassFile(parent, topic, method, name);

try {
Class<?> compiledClass = config.getListenerClassLoader().createClass(name.replace('/', '.'), listenerClassFile.toByteArray());

return (Listener)compiledClass.getConstructor()
.newInstance();
} catch (ReflectiveOperationException | IOException e) {
throw new RuntimeException(e);
}
}

/**
* Gets a unique method name from a method instance.
*
* @param method The method.
* @return The unique name.
*/
static String getUniqueMethodName(Method method) {
StringBuilder parameters = new StringBuilder();
for (Parameter parameter : method.getParameters()) {
parameters.append(parameter.getType().getName().replace('.', '_'));
}
return method.getName() + parameters.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package rip.hippo.lwjeb.configuration.invocation.impl;

import rip.hippo.lwjeb.configuration.config.impl.BusConfiguration;
import rip.hippo.lwjeb.configuration.invocation.ListenerFactory;
import rip.hippo.lwjeb.listener.Listener;

import java.lang.invoke.*;
import java.lang.reflect.Method;

/**
* @author Hippo
* @version 1.0.0, 3/18/21
* @since 5.2.0
*
* This uses {@link LambdaMetafactory} to create a {@link CallSite} to invoke the method.
*/
public final class LambdaMetaFactoryListenerFactory implements ListenerFactory {

/**
* Creates a listener that is invoked with {@link LambdaMetafactory}.
*
* @param parent The parent.
* @param method The method.
* @param topic The topic.
* @param config The bus configuration.
* @return The listener.
*/
@Override
public Listener create(Class<?> parent, Method method, Class<?> topic, BusConfiguration config) {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType invokedType = MethodType.methodType(Listener.class);
MethodHandle implMethod = lookup.unreflect(method);
MethodType instantiatedMethodType = implMethod.type();
MethodType samMethodType = instantiatedMethodType.changeParameterType(0, Object.class).changeParameterType(1, Object.class);

CallSite callSite = LambdaMetafactory.metafactory(lookup, "invoke", invokedType, samMethodType, implMethod, instantiatedMethodType);
return (Listener) callSite.getTarget().invoke();
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package rip.hippo.lwjeb.configuration.invocation.impl;

import rip.hippo.lwjeb.configuration.config.impl.BusConfiguration;
import rip.hippo.lwjeb.configuration.invocation.ListenerFactory;
import rip.hippo.lwjeb.listener.Listener;

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;

/**
* @author Hippo
* @version 1.0.0, 3/18/21
* @since 5.2.0
*
* A method handle listener factory creates listeners that are invoked via {@link MethodHandle}s.
*/
public final class MethodHandleListenerFactory implements ListenerFactory {

/**
* Creates a listener that is invoked with {@link MethodHandle}.
*
* @param parent The parent.
* @param method The method.
* @param topic The topic.
* @param config The bus configuration.
* @return The listener.
*/
@Override
public Listener create(Class<?> parent, Method method, Class<?> topic, BusConfiguration config) {
try {
MethodType methodType = MethodType.methodType(void.class, topic);
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(parent, method.getName(), methodType);

return (parentObject, topicObject) -> {
try {
methodHandle.invoke(parentObject, topicObject);
} catch (Throwable t) {
throw new RuntimeException(t);
}
};
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
Loading

0 comments on commit 1919ca8

Please sign in to comment.