    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}