// Copyright 2014 The Apache Software Foundation
//
// 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 org.apache.tapestry5.jcache.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
import javax.cache.annotation.CacheKeyGenerator;
import javax.cache.annotation.CacheResolverFactory;
import javax.inject.Singleton;
import org.apache.tapestry5.ioc.ObjectLocator;
import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.jsr107.ri.annotations.AbstractCacheLookupUtil;
import org.jsr107.ri.annotations.InternalCacheInvocationContext;
import org.jsr107.ri.annotations.InternalCacheKeyInvocationContext;
import org.jsr107.ri.annotations.StaticCacheInvocationContext;
import org.jsr107.ri.annotations.StaticCacheKeyInvocationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Adapted to Tapestry-IoC from the Guice implementation in the reference implementation.
*/
@Singleton
public class CacheLookupUtil extends AbstractCacheLookupUtil<MethodInvocation>
{
private static final Logger LOGGER = LoggerFactory.getLogger(CacheLookupUtil.class);
private static final Pattern SERVICE_PROXY_CLASS_NAME = Pattern.compile("\\$.+_[a-f0-9]+");
private final ObjectLocator objectLocator;
private final CacheKeyGenerator defaultCacheKeyGenerator;
private final CacheResolverFactory defaultCacheResolverFactory;
/**
* Single constructor of this class.
*
* @param defaultCacheKeyGenerator
* the default CacheKeyGenerator
* @param defaultCacheResolverFactory
* the default CacheResolverFactory
*/
public CacheLookupUtil(ObjectLocator objectLocator, CacheKeyGenerator defaultCacheKeyGenerator,
CacheResolverFactory defaultCacheResolverFactory)
{
this.objectLocator = objectLocator;
this.defaultCacheKeyGenerator = defaultCacheKeyGenerator;
this.defaultCacheResolverFactory = defaultCacheResolverFactory;
}
@SuppressWarnings(
{ "unchecked", "rawtypes" })
@Override
protected InternalCacheKeyInvocationContext<? extends Annotation> createCacheKeyInvocationContextImpl(
StaticCacheKeyInvocationContext<? extends Annotation> staticCacheKeyInvocationContext,
MethodInvocation invocation)
{
return new TapestryIoCInternalCacheKeyInvocationContext(staticCacheKeyInvocationContext,
invocation);
}
@SuppressWarnings(
{ "unchecked", "rawtypes" })
@Override
protected InternalCacheInvocationContext<? extends Annotation> createCacheInvocationContextImpl(
StaticCacheInvocationContext<? extends Annotation> staticCacheInvocationContext,
MethodInvocation invocation)
{
return new TapestryIoCInternalCacheInvocationContext(staticCacheInvocationContext,
invocation);
}
@Override
protected Class<?> getTargetClass(MethodInvocation invocation)
{
final Object instance = invocation.getInstance();
Class<?> clasz = instance.getClass();
// Here be dragons . . .
if (SERVICE_PROXY_CLASS_NAME.matcher(clasz.getName()).matches())
{
clasz = getDelegateType(instance);
}
return clasz;
}
// Here be bigger dragons . . .
private Class<?> getDelegateType(Object instance)
{
Object delegate = instance;
Class<?> clasz = delegate.getClass();
if (SERVICE_PROXY_CLASS_NAME.matcher(clasz.getName()).matches())
{
try
{
delegate = getDelegate(delegate, clasz);
// More than one advice causes proxies to be nested
clasz = getDelegateType(delegate);
}
catch (Exception e)
{
LOGGER.error("Exception while getting service implementation type", e);
throw new RuntimeException("Exception while getting service implementation type", e);
}
}
return clasz;
}
private Object getDelegate(Object instance, Class<?> clasz) throws IllegalAccessException,
InvocationTargetException
{
try
{
return clasz.getDeclaredMethod(PlasticProxyFactoryImpl.INTERNAL_GET_DELEGATE).invoke(
instance);
}
catch (Exception e)
{
throw new RuntimeException(String.format("Couldn't find method %s in %s",
PlasticProxyFactoryImpl.INTERNAL_GET_DELEGATE, instance.getClass().getName()));
}
}
@Override
protected Method getMethod(MethodInvocation invocation)
{
Method method = invocation.getMethod();
final Class<?> methodClass = method.getClass();
final Class<?> targetClass = getTargetClass(invocation);
if (methodClass != targetClass)
{
method = findMethod(method, targetClass);
}
return method;
}
private Method findMethod(final Method method, final Class<?> targetClass)
{
try
{
return targetClass.getMethod(method.getName(), (Class<?>[]) method.getParameterTypes());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
protected <T> T getObjectByType(Class<T> type)
{
return this.objectLocator.getObject(type, null);
}
@Override
protected CacheKeyGenerator getDefaultCacheKeyGenerator()
{
return this.defaultCacheKeyGenerator;
}
@Override
protected CacheResolverFactory getDefaultCacheResolverFactory()
{
return this.defaultCacheResolverFactory;
}
}