View Javadoc
1   /*
2    * Copyright (C) 2003-2010 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  /*
18   * Copyright (C) 2003-2010 eXo Platform SAS.
19   *
20   * This program is free software; you can redistribute it and/or
21   * modify it under the terms of the GNU Affero General Public License
22   * as published by the Free Software Foundation; either version 3
23   * of the License, or (at your option) any later version.
24   *
25   * This program is distributed in the hope that it will be useful,
26   * but WITHOUT ANY WARRANTY; without even the implied warranty of
27   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28   * GNU General Public License for more details.
29   *
30   * You should have received a copy of the GNU General Public License
31   * along with this program; if not, see<http://www.gnu.org/licenses/>.
32   */
33  package org.exoplatform.cs.statistics;
34  
35  import java.lang.reflect.Method;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.Map;
39  import java.util.TreeMap;
40  import org.aspectj.lang.JoinPoint;
41  import org.aspectj.lang.annotation.After;
42  import org.aspectj.lang.annotation.Aspect;
43  import org.aspectj.lang.annotation.Before;
44  import org.aspectj.lang.annotation.Pointcut;
45  import org.exoplatform.container.ExoContainer;
46  import org.exoplatform.container.ExoContainerContext;
47  import org.exoplatform.services.jcr.statistics.JCRStatisticsManager;
48  import org.exoplatform.services.jcr.statistics.Statistics;
49  import org.exoplatform.services.log.ExoLogger;
50  import org.exoplatform.services.log.Log;
51  
52  /**
53   * This aspect is used to collect all the statistics of all the methods of the JCR API.
54   * 
55   * Created by The eXo Platform SAS
56   * Author : Nicolas Filotto 
57   *          nicolas.filotto@exoplatform.com
58   * 26 mars 2010  
59   */
60  @Aspect
61  public abstract class JCRAPIAspect
62  {
63  
64     /**
65      * The logger
66      */
67     private static final Log LOG = ExoLogger.getLogger(JCRAPIAspect.class);
68     
69     /**
70      * The result of the mapping if the corresponding value cannot be found.
71      */
72     private static final Statistics UNKNOWN = new Statistics(null, "?");
73  
74     /**
75      * The flag that indicates if the aspect is initialized or not.
76      */
77     private static volatile boolean INITIALIZED;
78  
79     /**
80      * The list of all the interfaces for which we want statistics
81      */
82     private static Class<?>[] TARGET_INTERFACES;
83  
84     /**
85      * The mapping between the AspectJ signature and the target statistics
86      */
87     private static volatile Map<String, Statistics> MAPPING = Collections.unmodifiableMap(new HashMap<String, Statistics>());
88  
89     /**
90      * The list of all the statistics, one per method
91      */
92     private final static Map<String, Map<String, Statistics>> ALL_STATISTICS = new HashMap<String, Map<String, Statistics>>();
93  
94     /**
95      * Gives the name of the statistics from the given {@link Method} 
96      */
97     private static String getStatisticsName(Method m)
98     {
99        StringBuilder sb = new StringBuilder();
100       sb.append(m.getName());
101       sb.append('(');
102       Class<?>[] types = m.getParameterTypes();
103       if (types != null)
104       {
105          boolean first = true;
106          for (Class<?> type : types)
107          {
108             if (first)
109             {
110                first = false;
111             }
112             else
113             {
114                sb.append(", ");
115             }
116             sb.append(type.getSimpleName());
117          }
118       }
119       sb.append(')');
120       return sb.toString();
121    }
122 
123    /**
124     * Gives the corresponding statistics for the given target class and AspectJ signature
125     * @param target the target {@link Class}
126     * @param signature the AspectJ signature
127     * @return the related {@link Statistics} or <code>null</code> if it cannot be found
128     */
129    private static Statistics getStatistics(Class<?> target, String signature)
130    {
131       initIfNeeded();
132       Statistics statistics = MAPPING.get(signature);
133       if (statistics == null)
134       {
135          synchronized (JCRAPIAspect.class)
136          {
137             Class<?> interfaceClass = findInterface(target);
138             if (interfaceClass != null)
139             {
140                Map<String, Statistics> allStatistics = ALL_STATISTICS.get(interfaceClass.getSimpleName());
141                if (allStatistics != null)
142                {
143                   int index1 = signature.indexOf('(');
144                   int index = signature.substring(0, index1).lastIndexOf('.');
145                   String name = signature.substring(index + 1);
146                   statistics = allStatistics.get(name);
147                }
148             }
149             if (statistics == null)
150             {
151                statistics = UNKNOWN;
152             }
153             Map<String, Statistics> tempMapping = new HashMap<String, Statistics>(MAPPING);
154             tempMapping.put(signature, statistics);
155             MAPPING = Collections.unmodifiableMap(tempMapping);            
156          }
157       }
158       if (UNKNOWN.equals(statistics))
159       {
160          return null;
161       }
162       return statistics;
163    }
164 
165    /**
166     * Find the monitored interface from the target {@link Class}
167     * @param target the target {@link Class}
168     * @return the monitored interface, <code>null</code> otherwise
169     */
170    private static Class<?> findInterface(Class<?> target)
171    {
172       if (target == null)
173       {
174          return null;
175       }
176       Class<?>[] interfaces = target.getInterfaces();
177       if (interfaces != null)
178       {
179          for (Class<?> c : TARGET_INTERFACES)
180          {
181             for (Class<?> i : interfaces)
182             {
183                if (c.getName().equals(i.getName()))
184                {
185                   return c;
186                }
187             }
188          }
189       }
190       return findInterface(target.getSuperclass());
191    }
192 
193    /**
194     * Initializes the aspect if needed
195     */
196    private static void initIfNeeded()
197    {
198       if (!INITIALIZED)
199       {
200          synchronized (JCRAPIAspect.class)
201          {
202             if (!INITIALIZED)
203             {
204                ExoContainer container = ExoContainerContext.getTopContainer();
205                JCRAPIAspectConfig config = null;
206                if (container != null)
207                {
208                   config = (JCRAPIAspectConfig)container.getComponentInstanceOfType(JCRAPIAspectConfig.class);
209                }
210                if (config == null)
211                {
212                   TARGET_INTERFACES = new Class<?>[]{};
213                   LOG.warn("No interface to monitor could be found");
214                }
215                else
216                {
217                   TARGET_INTERFACES = config.getTargetInterfaces();
218                   for (Class<?> c : TARGET_INTERFACES)
219                   {
220                      Statistics global = new Statistics(null, "global");
221                      Map<String, Statistics> statistics = new TreeMap<String, Statistics>();
222                      Method[] methods = c.getMethods();
223                      for (Method m : methods)
224                      {
225                         String name = getStatisticsName(m);
226                         statistics.put(name, new Statistics(global, name));
227                      }
228                      JCRStatisticsManager.registerStatistics(c.getSimpleName(), global, statistics);
229                      ALL_STATISTICS.put(c.getSimpleName(), statistics);
230                   }
231                }
232                INITIALIZED = true;
233             }
234          }
235       }
236    }
237 
238    @Pointcut
239    abstract void JCRAPIPointcut();
240 
241    @Before("JCRAPIPointcut()")
242    public void begin(JoinPoint thisJoinPoint)
243    {
244       Statistics statistics =
245          getStatistics(thisJoinPoint.getTarget().getClass(), thisJoinPoint.getSignature().toString());
246       if (statistics != null)
247       {
248          statistics.begin();
249       }
250    }
251 
252    @After("JCRAPIPointcut()")
253    public void end(JoinPoint thisJoinPoint)
254    {
255       Statistics statistics =
256          getStatistics(thisJoinPoint.getTarget().getClass(), thisJoinPoint.getSignature().toString());
257       if (statistics != null)
258       {
259          statistics.end();
260       }
261    }
262 }