/* * Copyright 2010 Google Inc. * * 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 com.google.web.bindery.requestfactory.server; import com.google.gwt.rpc.server.Pair; import com.google.web.bindery.requestfactory.shared.BaseProxy; import com.google.web.bindery.requestfactory.shared.Locator; import com.google.web.bindery.requestfactory.shared.RequestContext; import com.google.web.bindery.requestfactory.shared.RequestFactory; import com.google.web.bindery.requestfactory.shared.ServiceLocator; import java.lang.ref.SoftReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * A cache for idempotent methods in {@link ServiceLayer}. The caching is * separate from {@link ReflectiveServiceLayer} so that the cache can be applied * to any decorators injected by the user. */ class ServiceLayerCache extends ServiceLayerDecorator { /** * ConcurrentHashMaps don't allow null keys or values, but sometimes we want * to cache a null value. */ private static final Object NULL_MARKER = new Object(); private static SoftReference<Map<Method, Map<Object, Object>>> methodCache; private static final Method createLocator; private static final Method createServiceInstance; private static final Method getDomainClassLoader; private static final Method getGetter; private static final Method getIdType; private static final Method getRequestReturnType; private static final Method getSetter; private static final Method requiresServiceLocator; private static final Method resolveClass; private static final Method resolveClientType; private static final Method resolveDomainClass; private static final Method resolveDomainMethod; private static final Method resolveLocator; private static final Method resolveRequestContext; private static final Method resolveRequestContextMethod; private static final Method resolveRequestFactory; private static final Method resolveServiceClass; private static final Method resolveServiceLocator; private static final Method resolveTypeToken; static { createLocator = getMethod("createLocator", Class.class); createServiceInstance = getMethod("createServiceInstance", Class.class); getDomainClassLoader = getMethod("getDomainClassLoader"); getGetter = getMethod("getGetter", Class.class, String.class); getIdType = getMethod("getIdType", Class.class); getRequestReturnType = getMethod("getRequestReturnType", Method.class); getSetter = getMethod("getSetter", Class.class, String.class); requiresServiceLocator = getMethod("requiresServiceLocator", Method.class, Method.class); resolveClass = getMethod("resolveClass", String.class); resolveClientType = getMethod("resolveClientType", Class.class, Class.class, boolean.class); resolveDomainClass = getMethod("resolveDomainClass", Class.class); resolveDomainMethod = getMethod("resolveDomainMethod", String.class); resolveLocator = getMethod("resolveLocator", Class.class); resolveRequestContext = getMethod("resolveRequestContext", String.class); resolveRequestContextMethod = getMethod("resolveRequestContextMethod", String.class); resolveRequestFactory = getMethod("resolveRequestFactory", String.class); resolveServiceClass = getMethod("resolveServiceClass", Class.class); resolveServiceLocator = getMethod("resolveServiceLocator", Class.class); resolveTypeToken = getMethod("resolveTypeToken", Class.class); } private static Map<Method, Map<Object, Object>> getCache() { Map<Method, Map<Object, Object>> toReturn = methodCache == null ? null : methodCache.get(); if (toReturn == null) { toReturn = new ConcurrentHashMap<Method, Map<Object, Object>>(); methodCache = new SoftReference<Map<Method, Map<Object, Object>>>(toReturn); } return toReturn; } private static Method getMethod(String name, Class<?>... argTypes) { try { return ServiceLayer.class.getMethod(name, argTypes); } catch (SecurityException e) { throw new RuntimeException("Could not set up ServiceLayerCache Methods", e); } catch (NoSuchMethodException e) { throw new RuntimeException("Could not set up ServiceLayerCache Methods", e); } } private final Map<Method, Map<Object, Object>> methodMap = getCache(); @Override public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) { return getOrCache(createLocator, clazz, clazz, clazz); } @Override public Object createServiceInstance(Class<? extends RequestContext> requestContext) { return getOrCache(createServiceInstance, requestContext, Object.class, requestContext); } @Override public ClassLoader getDomainClassLoader() { return getOrCache(getDomainClassLoader, NULL_MARKER, ClassLoader.class); } @Override public Method getGetter(Class<?> domainType, String property) { return getOrCache(getGetter, new Pair<Class<?>, String>(domainType, property), Method.class, domainType, property); } @Override public Class<?> getIdType(Class<?> domainType) { return getOrCache(getIdType, domainType, Class.class, domainType); } @Override public Type getRequestReturnType(Method contextMethod) { return getOrCache(getRequestReturnType, contextMethod, Type.class, contextMethod); } @Override public Method getSetter(Class<?> domainType, String property) { return getOrCache(getSetter, new Pair<Class<?>, String>(domainType, property), Method.class, domainType, property); } @Override public boolean requiresServiceLocator(Method contextMethod, Method domainMethod) { return getOrCache(requiresServiceLocator, new Pair<Method, Method>(contextMethod, domainMethod), Boolean.class, contextMethod, domainMethod); } @Override public Class<? extends BaseProxy> resolveClass(String typeToken) { Class<?> found = getOrCache(resolveClass, typeToken, Class.class, typeToken); return found.asSubclass(BaseProxy.class); } @Override public <T> Class<? extends T> resolveClientType(Class<?> domainClass, Class<T> clientType, boolean required) { Class<?> clazz = getOrCache(resolveClientType, new Pair<Class<?>, Class<?>>(domainClass, clientType), Class.class, domainClass, clientType, required); return clazz == null ? null : clazz.asSubclass(clientType); } @Override public Class<?> resolveDomainClass(Class<?> clazz) { return getOrCache(resolveDomainClass, clazz, Class.class, clazz); } @Override public Method resolveDomainMethod(String operation) { return getOrCache(resolveDomainMethod, operation, Method.class, operation); } @Override @SuppressWarnings("unchecked") public Class<? extends Locator<?, ?>> resolveLocator(Class<?> domainType) { return getOrCache(resolveLocator, domainType, Class.class, domainType); } @Override public Class<? extends RequestContext> resolveRequestContext(String operation) { Class<?> clazz = getOrCache(resolveRequestContext, operation, Class.class, operation); return clazz.asSubclass(RequestContext.class); } @Override public Method resolveRequestContextMethod(String operation) { return getOrCache(resolveRequestContextMethod, operation, Method.class, operation); } @Override public Class<? extends RequestFactory> resolveRequestFactory(String binaryName) { Class<?> clazz = getOrCache(resolveRequestFactory, binaryName, Class.class, binaryName); return clazz.asSubclass(RequestFactory.class); } @Override public Class<?> resolveServiceClass(Class<? extends RequestContext> requestContextClass) { return getOrCache(resolveServiceClass, requestContextClass, Class.class, requestContextClass); } @Override public Class<? extends ServiceLocator> resolveServiceLocator( Class<? extends RequestContext> requestContext) { Class<?> clazz = getOrCache(resolveServiceLocator, requestContext, Class.class, requestContext); return clazz == null ? null : clazz.asSubclass(ServiceLocator.class); } @Override public String resolveTypeToken(Class<? extends BaseProxy> domainClass) { return getOrCache(resolveTypeToken, domainClass, String.class, domainClass); } private <K, T> T getOrCache(Method method, K key, Class<T> valueType, Object... args) { Map<Object, Object> map = methodMap.get(method); if (map == null) { map = new ConcurrentHashMap<Object, Object>(); methodMap.put(method, map); } Object raw = map.get(key); if (raw == NULL_MARKER) { return null; } T toReturn = valueType.cast(raw); if (toReturn == null) { Throwable ex = null; try { toReturn = valueType.cast(method.invoke(getNext(), args)); map.put(key, toReturn == null ? NULL_MARKER : toReturn); } catch (InvocationTargetException e) { // The next layer threw an exception Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { // Re-throw RuntimeExceptions, which likely originate from die() throw ((RuntimeException) cause); } die(cause, "Unexpected checked exception"); } catch (IllegalArgumentException e) { ex = e; } catch (IllegalAccessException e) { ex = e; } if (ex != null) { die(ex, "Bad method invocation"); } } return toReturn; } }