View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.monitoring.reporting;
19  
20  import java.lang.reflect.Method;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.Stack;
26  import java.util.StringTokenizer;
27  
28  import org.apache.commons.monitoring.Monitor.Key;
29  
30  /**
31   * use a REST-style path to select a group of statValues. For example, to get
32   * the mean performance for monitors in category "services"
33   * <tt>/monitorsFromCategory/services/counter/performances/mean</tt>
34   *
35   * @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
36   */
37  public class Selector
38  {
39  
40      /**
41       *
42       */
43      private static final String SEP = "/";
44      private String path;
45  
46      /**
47       * Constructor
48       *
49       * @param path
50       */
51      public Selector( String path )
52      {
53          super();
54          this.path = path;
55      }
56  
57      /**
58       *
59       */
60      public Object select( Object resource )
61      {
62          Stack<String> stack = new Stack<String>();
63          StringTokenizer tokenizer = new StringTokenizer( path, SEP, true );
64          String previous = null;
65          while( tokenizer.hasMoreTokens() )
66          {
67              String next = tokenizer.nextToken();
68              if ( SEP.equals( next ) )
69              {
70                  if ( SEP.equals( previous ) )
71                  {
72                      stack.push( Key.DEFAULT );
73                  }
74              }
75              else
76              {
77                  stack.push( next );
78              }
79              previous = next;
80          }
81          if ( path.endsWith( SEP ) )
82          {
83              // get()
84              stack.push( "" );
85          }
86          Collections.reverse( stack );
87  
88          return select( resource, stack );
89      }
90  
91      @SuppressWarnings( "unchecked" )
92      protected Object select( Object resource, Stack<String> path )
93          throws IllegalArgumentException
94      {
95          String element = path.pop();
96          Method accessor = getAccessor( resource, element );
97          int i = accessor.getParameterTypes().length;
98          Object[] args = new Object[i];
99          if ( path.size() < i )
100         {
101             throw new IllegalArgumentException( "No enough arguments to call " + accessor );
102         }
103         for ( int j = 0; j < i; j++ )
104         {
105             args[j] = path.pop();
106         }
107         try
108         {
109             resource = accessor.invoke( resource, args );
110         }
111         catch ( Exception e )
112         {
113             throw new IllegalArgumentException( "Failed to invoke " + accessor );
114         }
115 
116         if ( resource instanceof Collection && !path.isEmpty() )
117         {
118             Collection input = (Collection) resource;
119             Collection result = new ArrayList( input.size() );
120             for ( Iterator iterator = input.iterator(); iterator.hasNext(); )
121             {
122                 Object sub = (Object) iterator.next();
123                 Stack<String> branch = new Stack<String>();
124                 branch.addAll( path );
125                 result.add( select( sub, branch ) );
126             }
127             path.clear();
128             resource = result;
129         }
130 
131         if ( !path.isEmpty() )
132         {
133             resource = select( resource, path );
134         }
135         return resource;
136     }
137 
138     /**
139      * Retrieve a getter method that only requires String parameters. When multiple methods
140      * match, the on with the most parameters is returned, for example
141      * getMonitor( String, String, String ) in preference to getMonitor( String )
142      *
143      * @param resource
144      * @param name
145      * @return
146      */
147     @SuppressWarnings( "unchecked" )
148     protected Method getAccessor( Object resource, String name )
149     {
150         String accessor = "get";
151         if ( name.length() > 0 )
152         {
153             accessor += Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 );
154         }
155         Method[] methods = resource.getClass().getMethods();
156         Method bestMatch = null;
157         for ( int i = 0; i < methods.length; i++ )
158         {
159             Method method = methods[i];
160             if ( method.getName().equals( accessor ) )
161             {
162                 Class[] parameters = method.getParameterTypes();
163                 boolean stringsOnly = true;
164                 for ( int j = 0; j < parameters.length; j++ )
165                 {
166                     if ( parameters[j] != String.class )
167                     {
168                         stringsOnly = false;
169                         break;
170                     }
171                 }
172                 if ( stringsOnly )
173                 {
174                     if ( bestMatch == null || bestMatch.getParameterTypes().length < method.getParameterTypes().length )
175                     {
176                         bestMatch = method;
177                     }
178                 }
179             }
180         }
181         if ( bestMatch == null )
182         {
183             throw new IllegalArgumentException( "No accessor for " + name + " on resource " + resource );
184         }
185         return bestMatch;
186     }
187 }