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}