/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openengsb.core.services.internal; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.reflect.MethodUtils; import org.openengsb.core.api.OsgiUtilsService; import org.openengsb.core.api.context.ContextHolder; import org.openengsb.core.api.remote.CustomJsonMarshaller; import org.openengsb.core.api.remote.CustomMarshallerRealTypeAccess; import org.openengsb.core.api.remote.MethodCall; import org.openengsb.core.api.remote.MethodResult; import org.openengsb.core.api.remote.MethodResult.ReturnType; import org.openengsb.core.api.remote.RequestHandler; import org.openengsb.core.api.remote.UseCustomJasonMarshaller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Throwables; public class RequestHandlerImpl implements RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandlerImpl.class); private OsgiUtilsService utilsService; @Override public MethodResult handleCall(MethodCall call) { Map<String, String> metaData = call.getMetaData(); String contextId = metaData.get("contextId"); if (contextId != null) { ContextHolder.get().setCurrentContextId(contextId); } Object service = retrieveOpenEngSBService(call); Method method = findMethod(service, call.getMethodName(), getArgTypes(call)); Object[] args = retrieveArguments(call, method); MethodResult methodResult = invokeMethod(service, method, args); methodResult.setMetaData(call.getMetaData()); return methodResult; } private Object[] retrieveArguments(MethodCall call, Method method) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Object[] originalArgs = call.getArgs(); for (int i = 0; i < originalArgs.length; i++) { Annotation[] currentArgAnnotations = parameterAnnotations[i]; Class<? extends CustomJsonMarshaller<?>> transformationAnnotation = searchForTransformationAnnotation(currentArgAnnotations); if (transformationAnnotation == null) { continue; } CustomJsonMarshaller<?> transformationInstance = createTransformationInstance(transformationAnnotation); originalArgs[i] = transformationInstance.transformArg(originalArgs[i]); } return originalArgs; } private CustomJsonMarshaller<?> createTransformationInstance( Class<? extends CustomJsonMarshaller<?>> transformationAnnotation) { try { return transformationAnnotation.newInstance(); } catch (Exception e) { throw new IllegalStateException("It's not possible to create transformation because of " + Throwables.getStackTraceAsString(e)); } } private Class<? extends CustomJsonMarshaller<?>> searchForTransformationAnnotation( Annotation[] currentArgAnnotations) { for (Annotation annotation : currentArgAnnotations) { if (annotation instanceof UseCustomJasonMarshaller) { return ((UseCustomJasonMarshaller) annotation).value(); } } return null; } private Object retrieveOpenEngSBService(MethodCall call) { Map<String, String> metaData = call.getMetaData(); String serviceId = metaData.get("serviceId"); String filter = metaData.get("serviceFilter"); String filterString = createFilterString(filter, serviceId); return utilsService.getService(filterString); } private String createFilterString(String filter, String serviceId) { if (filter == null) { if (serviceId == null) { throw new IllegalArgumentException("must specify either filter or serviceId"); } return String.format("(%s=%s)", org.osgi.framework.Constants.SERVICE_PID, serviceId); } else { if (serviceId == null) { return filter; } return String.format("(&%s(%s=%s))", filter, org.osgi.framework.Constants.SERVICE_PID, serviceId); } } private MethodResult invokeMethod(Object service, Method method, Object[] args) { MethodResult returnTemplate = new MethodResult(); try { Object result = method.invoke(service, args); if (method.getReturnType().getName().equals("void")) { returnTemplate.setType(ReturnType.Void); } else { returnTemplate.setType(ReturnType.Object); returnTemplate.setArg(result); String resultClassName = result == null ? method.getReturnType().getName() : result.getClass().getName(); returnTemplate.setClassName(resultClassName); } } catch (Exception e) { LOGGER.warn("Exception in remote method invocation: ", e); returnTemplate.setType(ReturnType.Exception); if (e.getClass().equals(InvocationTargetException.class)) { e = (Exception) e.getCause(); // if it's not an Exception we are in REAL trouble anyway } returnTemplate.setArg(e.getCause()); returnTemplate.setClassName(e.getClass().getName()); } return returnTemplate; } private Method findMethod(Object service, String methodName, Class<?>[] argTypes) { Method method; Class<?> serviceClass = retrieveRealServiceClass(service); if (serviceClass.isInstance(CustomMarshallerRealTypeAccess.class)) { serviceClass = ((CustomMarshallerRealTypeAccess) service).getRealUnproxiedType(); } method = MethodUtils.getMatchingAccessibleMethod(serviceClass, methodName, argTypes); if (method == null) { throw new IllegalArgumentException(String.format("could not find method matching arguments \"%s(%s)\"", methodName, ArrayUtils.toString(argTypes))); } return method; } /** * TODO: OPENENGSB-1976 * * This is a workaround for the mess with Aries JPA proxies. */ private Class<?> retrieveRealServiceClass(Object service) { Class<?> serviceClass = service.getClass(); Method realTypeMethod = null; try { realTypeMethod = serviceClass.getMethod("getRealUnproxiedType"); } catch (NoSuchMethodException e) { // no problem this method does not have to exist } if (realTypeMethod != null) { try { serviceClass = (Class<?>) realTypeMethod.invoke(service); } catch (Exception e) { e.printStackTrace(); } } return serviceClass; } private Class<?>[] getArgTypes(MethodCall args) { List<Class<?>> clazzes = new ArrayList<Class<?>>(); for (String clazz : args.getClasses()) { try { clazzes.add(ClassUtils.getClass(this.getClass().getClassLoader(), clazz)); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("The classes defined could not be found", e); } } return clazzes.toArray(new Class<?>[0]); } public void setUtilsService(OsgiUtilsService utilsService) { this.utilsService = utilsService; } }