Skip to content

Commit

Permalink
Use plug-in method handlers in microservice proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
livthomas authored and SlavoKrupa committed Nov 5, 2016
1 parent b7164c2 commit 742640d
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 113 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* -----------------------------------------------------------------------\
* SilverWare
*  
* Copyright (C) 2016 the original author or authors.
*  
* 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 io.silverware.microservices.providers.cdi.internal;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Priority;

import io.silverware.microservices.MicroserviceMetaData;
import io.silverware.microservices.annotations.MicroserviceReference;
import io.silverware.microservices.silver.services.LookupStrategy;
import io.silverware.microservices.silver.services.LookupStrategyFactory;

/**
* Default microservice method handler which is invoked as the last one and makes an actual call on the service instance.
*/
@Priority(Integer.MAX_VALUE)
public class DefaultMethodHandler extends MicroserviceMethodHandler {

private static final Logger log = LogManager.getLogger(DefaultMethodHandler.class);

private final MicroserviceProxyBean proxyBean;

private final LookupStrategy lookupStrategy;

protected DefaultMethodHandler(final MicroserviceProxyBean proxyBean) throws Exception {
this.proxyBean = proxyBean;

final Set<Annotation> qualifiers = proxyBean.getQualifiers().stream().filter(qualifier -> !qualifier.annotationType().getName().equals(MicroserviceReference.class.getName())).collect(Collectors.toSet());
final MicroserviceMetaData metaData = new MicroserviceMetaData(proxyBean.getMicroserviceName(), proxyBean.getServiceInterface(), qualifiers, proxyBean.getAnnotations());

this.lookupStrategy = LookupStrategyFactory.getStrategy(proxyBean.getContext(), metaData, proxyBean.getAnnotations());
}

private synchronized Object getService() {
final Object service = lookupStrategy.getService();

if (log.isDebugEnabled()) {
log.debug(String.format("Proxy %s matched with service implementation %s.", this.toString(), service));
}

return service;
}

@Override
public Object invoke(final Method method, final Object[] args) throws Exception {
if (method.getDeclaringClass() == Object.class) {
final String methodName = method.getName();
final int paramCount = method.getParameterTypes().length;

if ("toString".equals(methodName) && paramCount == 0) {
return "Microservices proxy for " + proxyBean.getServiceInterface().getName();
} else if ("equals".equals(methodName) && paramCount == 1) {
return this.equals(args[0]);
} else if ("hashCode".equals(methodName) && paramCount == 0) {
return this.hashCode();
} else if ("getClass".equals(methodName) && paramCount == 0) {
return proxyBean.getServiceInterface();
}
}

if (log.isDebugEnabled()) {
log.debug("Invocation of " + method);
}

Object service = getService();
return method.invoke(service, args);
}

@Override
public MicroserviceProxyBean getProxyBean() {
return proxyBean;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* -----------------------------------------------------------------------\
* SilverWare
*  
* Copyright (C) 2016 the original author or authors.
*  
* 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 io.silverware.microservices.providers.cdi.internal;

import java.lang.reflect.Method;

import javassist.util.proxy.MethodHandler;

/**
* Method handler for invoking microservice methods.
*
* Descendants need to have a constructor with a single parameter of type MicroserviceMethodHandler (the next method handler).
* The implementations of methods should call the same method on the next method handler but they can alter its behaviour by making some additional changes.
* Descendants also need to specify the priority on their classes. The higher it is, the later they are invoked (closer to the actual service call).
*
* @see javax.annotation.Priority
*/
public abstract class MicroserviceMethodHandler implements MethodHandler {

public static final int DEFAULT_PRIORITY = 100;

@Override
public final Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable {
return invoke(thisMethod, args);
}

public abstract Object invoke(Method method, Object[] args) throws Exception;

public abstract MicroserviceProxyBean getProxyBean();

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* -----------------------------------------------------------------------\
* SilverWare
*  
* Copyright (C) 2010 - 2013 the original author or authors.
* Copyright (C) 2016 the original author or authors.
*  
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -94,7 +94,7 @@ public MicroserviceProxyBean(final String microserviceName, final Class<?> proxy
this.qualifiers = new HashSet<>(qualifiers);
this.annotations = annotations;

proxyBean = MicroserviceProxy.getProxy(this);
proxyBean = MicroserviceProxyFactory.createProxy(this);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* -----------------------------------------------------------------------\
* SilverWare
*  
* Copyright (C) 2016 the original author or authors.
*  
* 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 io.silverware.microservices.providers.cdi.internal;

import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.util.ConfigurationBuilder;

import java.lang.reflect.Constructor;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Priority;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

/**
* Creates a proxy for invoking microservice methods.
*
* A classpath is searched for additional method handlers which are executed before DefaultMethodHandler ordered by their priority.
*
* @see io.silverware.microservices.providers.cdi.internal.MicroserviceMethodHandler
* @see io.silverware.microservices.providers.cdi.internal.DefaultMethodHandler
*/
public class MicroserviceProxyFactory {

private static final List<Class<? extends MicroserviceMethodHandler>> HANDLER_CLASSES;

static {
ConfigurationBuilder builder = ConfigurationBuilder.build("").addScanners(new ResourcesScanner());
Reflections reflections = new Reflections(builder);

HANDLER_CLASSES = reflections.getSubTypesOf(MicroserviceMethodHandler.class)
.stream()
.filter(handler -> handler != DefaultMethodHandler.class)
.sorted(new MethodHandlerPrioritizer())
.collect(Collectors.toList());
}

private MicroserviceProxyFactory() {
}

/**
* Creates a proxy for a given proxy bean.
*
* @param proxyBean
* proxy bean for which a proxy is being created
* @param <T>
* provided service type
* @return proxy
*/
public static <T> T createProxy(final MicroserviceProxyBean proxyBean) {
try {
ProxyFactory factory = new ProxyFactory();
if (proxyBean.getServiceInterface().isInterface()) {
factory.setInterfaces(new Class[] { proxyBean.getServiceInterface() });
} else {
factory.setSuperclass(proxyBean.getServiceInterface());
}
MethodHandler methodHandler = createMethodHandler(proxyBean);
return (T) factory.create(new Class[0], new Object[0], methodHandler);
} catch (Exception e) {
throw new IllegalStateException("Cannot create proxy for class " + proxyBean.getServiceInterface().getName() + ": ", e);
}
}

private static MethodHandler createMethodHandler(MicroserviceProxyBean parentBean) throws Exception {
MicroserviceMethodHandler methodHandler = new DefaultMethodHandler(parentBean);
for (Class<? extends MicroserviceMethodHandler> handlerClass : HANDLER_CLASSES) {
final Constructor c = handlerClass.getConstructor(MicroserviceMethodHandler.class);
methodHandler = (MicroserviceMethodHandler) c.newInstance(methodHandler);
}
return methodHandler;
}

private static class MethodHandlerPrioritizer implements Comparator<Class<? extends MicroserviceMethodHandler>> {

@Override
public int compare(final Class<? extends MicroserviceMethodHandler> class1, final Class<? extends MicroserviceMethodHandler> class2) {
return getPriority(class2) - getPriority(class1);
}

private int getPriority(Class<? extends MicroserviceMethodHandler> methodHandlerClass) {
Priority priority = methodHandlerClass.getAnnotation(Priority.class);
return priority != null ? priority.value() : MicroserviceMethodHandler.DEFAULT_PRIORITY;
}

}

}

0 comments on commit 742640d

Please sign in to comment.