001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.increment;
016    
017    import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
018    import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
019    import com.liferay.portal.kernel.increment.BufferedIncrement;
020    import com.liferay.portal.kernel.increment.BufferedIncrementThreadLocal;
021    import com.liferay.portal.kernel.increment.Increment;
022    import com.liferay.portal.kernel.increment.IncrementFactory;
023    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackRegistryUtil;
024    import com.liferay.portal.kernel.util.StringUtil;
025    import com.liferay.portal.spring.aop.AnnotationChainableMethodAdvice;
026    
027    import java.io.Serializable;
028    
029    import java.lang.annotation.Annotation;
030    import java.lang.reflect.Method;
031    
032    import java.util.Map;
033    import java.util.concurrent.Callable;
034    import java.util.concurrent.ConcurrentHashMap;
035    import java.util.concurrent.ConcurrentMap;
036    
037    import org.aopalliance.intercept.MethodInvocation;
038    
039    /**
040     * @author Zsolt Berentey
041     * @author Shuyang Zhou
042     */
043    public class BufferedIncrementAdvice
044            extends AnnotationChainableMethodAdvice<BufferedIncrement> {
045    
046            @Override
047            @SuppressWarnings("rawtypes")
048            public Object before(MethodInvocation methodInvocation) throws Throwable {
049                    BufferedIncrement bufferedIncrement = findAnnotation(methodInvocation);
050    
051                    if (!BufferedIncrementThreadLocal.isEnabled() ||
052                            (bufferedIncrement == _nullBufferedIncrement)) {
053    
054                            return null;
055                    }
056    
057                    String configuration = bufferedIncrement.configuration();
058    
059                    BufferedIncrementConfiguration bufferedIncrementConfiguration =
060                            _bufferedIncrementConfigurations.get(configuration);
061    
062                    if (bufferedIncrementConfiguration == null) {
063                            bufferedIncrementConfiguration = new BufferedIncrementConfiguration(
064                                    configuration);
065    
066                            _bufferedIncrementConfigurations.put(
067                                    configuration, bufferedIncrementConfiguration);
068                    }
069    
070                    if (!bufferedIncrementConfiguration.isEnabled()) {
071                            return nullResult;
072                    }
073    
074                    Method method = methodInvocation.getMethod();
075    
076                    BufferedIncrementProcessor bufferedIncrementProcessor =
077                            _bufferedIncrementProcessors.get(method);
078    
079                    if (bufferedIncrementProcessor == null) {
080                            bufferedIncrementProcessor = new BufferedIncrementProcessor(
081                                    bufferedIncrementConfiguration, method);
082    
083                            BufferedIncrementProcessor previousBufferedIncrementProcessor =
084                                    _bufferedIncrementProcessors.putIfAbsent(
085                                            method, bufferedIncrementProcessor);
086    
087                            if (previousBufferedIncrementProcessor != null) {
088                                    bufferedIncrementProcessor = previousBufferedIncrementProcessor;
089                            }
090                    }
091    
092                    Object[] arguments = methodInvocation.getArguments();
093    
094                    Object value = arguments[arguments.length - 1];
095    
096                    CacheKeyGenerator cacheKeyGenerator =
097                            CacheKeyGeneratorUtil.getCacheKeyGenerator(
098                                    BufferedIncrementAdvice.class.getName());
099    
100                    for (int i = 0; i < arguments.length - 1; i++) {
101                            cacheKeyGenerator.append(StringUtil.toHexString(arguments[i]));
102                    }
103    
104                    Serializable batchKey = cacheKeyGenerator.finish();
105    
106                    Increment<?> increment = IncrementFactory.createIncrement(
107                            bufferedIncrement.incrementClass(), value);
108    
109                    final BufferedIncrementProcessor callbackBufferedIncrementProcessor =
110                            bufferedIncrementProcessor;
111    
112                    final BufferedIncreasableEntry bufferedIncreasableEntry =
113                            new BufferedIncreasableEntry(methodInvocation, batchKey, increment);
114    
115                    TransactionCommitCallbackRegistryUtil.registerCallback(
116                            new Callable<Void>() {
117    
118                                    @Override
119                                    public Void call() throws Exception {
120                                            callbackBufferedIncrementProcessor.process(
121                                                    bufferedIncreasableEntry);
122    
123                                            return null;
124                                    }
125    
126                            });
127    
128                    return nullResult;
129            }
130    
131            public void destroy() {
132                    for (BufferedIncrementProcessor bufferedIncrementProcessor :
133                                    _bufferedIncrementProcessors.values()) {
134    
135                            bufferedIncrementProcessor.destroy();
136                    }
137            }
138    
139            @Override
140            public BufferedIncrement getNullAnnotation() {
141                    return _nullBufferedIncrement;
142            }
143    
144            private static BufferedIncrement _nullBufferedIncrement =
145                    new BufferedIncrement() {
146    
147                            @Override
148                            public Class<? extends Annotation> annotationType() {
149                                    return BufferedIncrement.class;
150                            }
151    
152                            @Override
153                            public String configuration() {
154                                    return "default";
155                            }
156    
157                            @Override
158                            public Class<? extends Increment<?>> incrementClass() {
159                                    return null;
160                            }
161    
162                    };
163    
164            private Map<String, BufferedIncrementConfiguration>
165                    _bufferedIncrementConfigurations =
166                            new ConcurrentHashMap<String, BufferedIncrementConfiguration>();
167            private ConcurrentMap<Method, BufferedIncrementProcessor>
168                    _bufferedIncrementProcessors =
169                            new ConcurrentHashMap<Method, BufferedIncrementProcessor>();
170    
171    }