001// Copyright 2014 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014package org.apache.tapestry5.jcache.internal; 015 016import java.lang.annotation.Annotation; 017import java.lang.reflect.InvocationTargetException; 018import java.lang.reflect.Method; 019import java.util.regex.Pattern; 020 021import javax.cache.annotation.CacheKeyGenerator; 022import javax.cache.annotation.CacheResolverFactory; 023import javax.inject.Singleton; 024 025import org.apache.tapestry5.ioc.ObjectLocator; 026import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl; 027import org.apache.tapestry5.plastic.MethodInvocation; 028import org.jsr107.ri.annotations.AbstractCacheLookupUtil; 029import org.jsr107.ri.annotations.InternalCacheInvocationContext; 030import org.jsr107.ri.annotations.InternalCacheKeyInvocationContext; 031import org.jsr107.ri.annotations.StaticCacheInvocationContext; 032import org.jsr107.ri.annotations.StaticCacheKeyInvocationContext; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * Adapted to Tapestry-IoC from the Guice implementation in the reference implementation. 038 */ 039@Singleton 040public class CacheLookupUtil extends AbstractCacheLookupUtil<MethodInvocation> 041{ 042 043 private static final Logger LOGGER = LoggerFactory.getLogger(CacheLookupUtil.class); 044 045 private static final Pattern SERVICE_PROXY_CLASS_NAME = Pattern.compile("\\$.+_[a-f0-9]+"); 046 047 private final ObjectLocator objectLocator; 048 private final CacheKeyGenerator defaultCacheKeyGenerator; 049 private final CacheResolverFactory defaultCacheResolverFactory; 050 051 /** 052 * Single constructor of this class. 053 * 054 * @param defaultCacheKeyGenerator 055 * the default CacheKeyGenerator 056 * @param defaultCacheResolverFactory 057 * the default CacheResolverFactory 058 */ 059 public CacheLookupUtil(ObjectLocator objectLocator, CacheKeyGenerator defaultCacheKeyGenerator, 060 CacheResolverFactory defaultCacheResolverFactory) 061 { 062 this.objectLocator = objectLocator; 063 this.defaultCacheKeyGenerator = defaultCacheKeyGenerator; 064 this.defaultCacheResolverFactory = defaultCacheResolverFactory; 065 } 066 067 @SuppressWarnings( 068 { "unchecked", "rawtypes" }) 069 @Override 070 protected InternalCacheKeyInvocationContext<? extends Annotation> createCacheKeyInvocationContextImpl( 071 StaticCacheKeyInvocationContext<? extends Annotation> staticCacheKeyInvocationContext, 072 MethodInvocation invocation) 073 { 074 return new TapestryIoCInternalCacheKeyInvocationContext(staticCacheKeyInvocationContext, 075 invocation); 076 } 077 078 @SuppressWarnings( 079 { "unchecked", "rawtypes" }) 080 @Override 081 protected InternalCacheInvocationContext<? extends Annotation> createCacheInvocationContextImpl( 082 StaticCacheInvocationContext<? extends Annotation> staticCacheInvocationContext, 083 MethodInvocation invocation) 084 { 085 return new TapestryIoCInternalCacheInvocationContext(staticCacheInvocationContext, 086 invocation); 087 } 088 089 @Override 090 protected Class<?> getTargetClass(MethodInvocation invocation) 091 { 092 final Object instance = invocation.getInstance(); 093 Class<?> clasz = instance.getClass(); 094 // Here be dragons . . . 095 if (SERVICE_PROXY_CLASS_NAME.matcher(clasz.getName()).matches()) 096 { 097 clasz = getDelegateType(instance); 098 } 099 return clasz; 100 } 101 102 // Here be bigger dragons . . . 103 private Class<?> getDelegateType(Object instance) 104 { 105 Object delegate = instance; 106 Class<?> clasz = delegate.getClass(); 107 if (SERVICE_PROXY_CLASS_NAME.matcher(clasz.getName()).matches()) 108 { 109 try 110 { 111 delegate = getDelegate(delegate, clasz); 112 // More than one advice causes proxies to be nested 113 clasz = getDelegateType(delegate); 114 } 115 catch (Exception e) 116 { 117 LOGGER.error("Exception while getting service implementation type", e); 118 throw new RuntimeException("Exception while getting service implementation type", e); 119 } 120 } 121 return clasz; 122 } 123 124 private Object getDelegate(Object instance, Class<?> clasz) throws IllegalAccessException, 125 InvocationTargetException 126 { 127 try 128 { 129 return clasz.getDeclaredMethod(PlasticProxyFactoryImpl.INTERNAL_GET_DELEGATE).invoke( 130 instance); 131 } 132 catch (Exception e) 133 { 134 throw new RuntimeException(String.format("Couldn't find method %s in %s", 135 PlasticProxyFactoryImpl.INTERNAL_GET_DELEGATE, instance.getClass().getName())); 136 } 137 } 138 139 @Override 140 protected Method getMethod(MethodInvocation invocation) 141 { 142 Method method = invocation.getMethod(); 143 final Class<?> methodClass = method.getClass(); 144 final Class<?> targetClass = getTargetClass(invocation); 145 if (methodClass != targetClass) 146 { 147 method = findMethod(method, targetClass); 148 } 149 return method; 150 } 151 152 private Method findMethod(final Method method, final Class<?> targetClass) 153 { 154 try 155 { 156 return targetClass.getMethod(method.getName(), (Class<?>[]) method.getParameterTypes()); 157 } 158 catch (Exception e) 159 { 160 throw new RuntimeException(e); 161 } 162 } 163 164 @Override 165 protected <T> T getObjectByType(Class<T> type) 166 { 167 return this.objectLocator.getObject(type, null); 168 } 169 170 @Override 171 protected CacheKeyGenerator getDefaultCacheKeyGenerator() 172 { 173 return this.defaultCacheKeyGenerator; 174 } 175 176 @Override 177 protected CacheResolverFactory getDefaultCacheResolverFactory() 178 { 179 return this.defaultCacheResolverFactory; 180 } 181}