/* * Copyright (c) 2016 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.jersey.protobuf; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import javax.ws.rs.WebApplicationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.Message; import com.google.protobuf.Message.Builder; public abstract class AbstractProtobufProvider { private static final Logger log = LoggerFactory.getLogger(AbstractProtobufProvider.class); private final BuilderFactory builderFactory = new BuilderFactory(); private final ExtensionRegistryFactory extensionRegistryFactory = new ExtensionRegistryFactory(); BuilderFactory builders() { return builderFactory; } ExtensionRegistryFactory extensions() { return extensionRegistryFactory; } @SuppressWarnings("unchecked") Class<Message> extractMessageType(Class<?> type, Type genericType) { return isWrapped(type, genericType) ? Types.getCollectionBaseType(type, genericType) : (Class<Message>) type; } boolean isWrapped(Class<?> type, Type genericType) { if((Iterable.class.isAssignableFrom(type) || type.isArray()) && genericType != null) { Class<?> baseType = Types.getCollectionBaseType(type, genericType); return baseType != null && Message.class.isAssignableFrom(baseType); } return false; } protected static final class DescriptorFactory { private final Map<Class<?>, Method> methodCache = new HashMap<>(); Descriptor forMessage(Class<Message> messageType) { if(messageType == null) throw new IllegalArgumentException("messageType cannot be null"); return (Descriptor) invokeStaticMethod(extractStaticMethod("getDescriptor", methodCache, messageType)); } } protected static final class ExtensionRegistryFactory { private final Map<Class<?>, ExtensionRegistry> registryCache = new HashMap<>(); private final Map<Class<?>, Method> methodCache = new HashMap<>(); ExtensionRegistry forMessage(Class<Message> messageType) { if(messageType == null) throw new IllegalArgumentException("messageType cannot be null"); Class<?> enclosingType = messageType.getEnclosingClass(); if(!registryCache.containsKey(enclosingType)) { ExtensionRegistry registry = ExtensionRegistry.newInstance(); invokeStaticMethod(extractStaticMethod("registerAllExtensions", methodCache, messageType.getEnclosingClass(), ExtensionRegistry.class), registry); registryCache.put(enclosingType, registry); } return registryCache.get(enclosingType); } } protected static final class BuilderFactory { private final Map<Class<?>, Method> methodCache = new HashMap<>(); Builder forMessage(Class<Message> messageType) { if(messageType == null) throw new IllegalArgumentException("messageType cannot be null"); return (Builder) invokeStaticMethod(extractStaticMethod("newBuilder", methodCache, messageType)); } } private static Object invokeStaticMethod(Method method, Object... arguments) { if(method == null) throw new IllegalArgumentException("method cannot be null"); try { return method.invoke(null, arguments); } catch(WebApplicationException e) { throw e; } catch(RuntimeException | InvocationTargetException | IllegalAccessException e) { log.error("Error invoking '{}' method for type {}", method.getName(), method.getDeclaringClass().getName(), e); throw new WebApplicationException(500); } } private static Method extractStaticMethod(String methodName, Map<Class<?>, Method> methodCache, Class<?> type, Class<?>... arguments) { if(methodName == null) throw new IllegalArgumentException("methodName cannot be null"); if(methodCache == null) throw new IllegalArgumentException("methodCache cannot be null"); if(type == null) throw new IllegalArgumentException("type cannot be null"); if(!methodCache.containsKey(type)) { try { methodCache.put(type, type.getMethod(methodName, arguments)); } catch(SecurityException e) { log.error("Error getting '{}' method from type {}", methodName, type.getName(), e); throw new WebApplicationException(500); } catch(NoSuchMethodException e) { throw new IllegalStateException( "The type " + type.getName() + " does not define a '" + methodName + "' static method."); } } return methodCache.get(type); } }