/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.transport.dispatcher;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.TreeMap;
import org.openecard.common.interfaces.Dispatchable;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.common.interfaces.DispatcherException;
import org.openecard.common.interfaces.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the {@code Dispatcher} interface.
* This implementation defers its actual reflection work to the {@link Service} class.
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class MessageDispatcher implements Dispatcher {
private static final Logger logger = LoggerFactory.getLogger(MessageDispatcher.class);
private final Environment environment;
/** Key is parameter classname */
private final TreeMap<String, Service> serviceMap;
/** Key is service interface classname */
private final TreeMap<String, Method> serviceInstMap;
/**
* Creates a new MessageDispatcher instance and loads all definitions from the webservice interfaces in the
* environment.
*
* @param environment The environment with the webservice interface getters.
*/
public MessageDispatcher(Environment environment) {
this.environment = environment;
serviceMap = new TreeMap<String, Service>();
serviceInstMap = new TreeMap<String, Method>();
initDefinitions();
}
@Override
public Object deliver(Object req) throws DispatcherException, InvocationTargetException {
try {
Class<?> reqClass = req.getClass();
Service s = getService(reqClass);
Object serviceImpl = getServiceImpl(s);
logger.debug("Delivering message of type: {}", req.getClass().getName());
Object result = s.invoke(serviceImpl, req);
return result;
} catch (IllegalAccessException ex) {
throw new DispatcherException(ex);
} catch (IllegalArgumentException ex) {
throw new DispatcherException(ex);
}
}
private Service getService(Class<?> reqClass) throws IllegalAccessException {
if (! serviceMap.containsKey(reqClass.getName())) {
String msg = "No service with a method containing parameter type " + reqClass.getName() + " present.";
throw new IllegalAccessException(msg);
}
return serviceMap.get(reqClass.getName());
}
private Object getServiceImpl(Service s) throws IllegalAccessException, InvocationTargetException {
Method m = serviceInstMap.get(s.getServiceInterface().getName());
if (m == null) {
String msg = "The environment does not contain a service for class " + s.getServiceInterface().getName();
throw new IllegalAccessException(msg);
}
Object impl = m.invoke(environment);
return impl;
}
private void initDefinitions() {
// load all annotated service methods from environment
Class<?> envClass = this.environment.getClass();
Method[] envMethods = envClass.getMethods();
// loop over methods and build index structure
for (Method nextAccessor : envMethods) {
// is the method annotated?
if (nextAccessor.getAnnotation(Dispatchable.class) != null) {
// check access rights and stuff
int modifier = nextAccessor.getModifiers();
if (Modifier.isAbstract(modifier)) {
continue;
} else if (! Modifier.isPublic(modifier)) {
continue;
} else if (Modifier.isStatic(modifier)) {
continue;
}
// try to read class from annotation, if not take return value
Dispatchable methodAnnotation = nextAccessor.getAnnotation(Dispatchable.class);
Class<?> returnType = methodAnnotation.interfaceClass();
// check if the service is already defined
if (this.serviceInstMap.containsKey(returnType.getName())) {
String msg = "Omitting service type {}, because its type already associated with another service.";
logger.warn(msg, returnType.getName());
continue;
}
// add env method mapping
this.serviceInstMap.put(returnType.getName(), nextAccessor);
// create service and map its request parameters
Service service = new Service(returnType);
for (Class<?> reqClass : service.getRequestClasses()) {
if (serviceMap.containsKey(reqClass.getName())) {
String msg = "Omitting method with parameter type {} in service interface {} because its ";
msg += "type already associated with another service.";
logger.warn(msg, reqClass.getName(), returnType.getName());
} else {
serviceMap.put(reqClass.getName(), service);
}
}
}
}
}
}