View Javadoc

1   /*
2    ~ Copyright 2006-2007 Nicolas De Loof.
3    ~
4    ~ Licensed under the Apache License, Version 2.0 (the "License");
5    ~ you may not use this file except in compliance with the License.
6    ~ You may obtain a copy of the License at
7    ~
8    ~      http://www.apache.org/licenses/LICENSE-2.0
9    ~
10   ~ Unless required by applicable law or agreed to in writing, software
11   ~ distributed under the License is distributed on an "AS IS" BASIS,
12   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   ~ See the License for the specific language governing permissions and
14   ~ limitations under the License.
15   */
16  package org.jmonit.web;
17  
18  import java.io.IOException;
19  import java.lang.reflect.Array;
20  import java.lang.reflect.Method;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.StringTokenizer;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import org.jmonit.Monitoring;
34  import org.jmonit.reporting.JSonRenderer;
35  import org.jmonit.reporting.Renderer;
36  import org.jmonit.spi.PluginManager;
37  
38  /**
39   * Access to the repository datas using REST URLs. Inspired by stapler to
40   * convert a REST path to navigation in Java object graph, but dedicated to
41   * monitors and features handling.
42   * 
43   * @see https://stapler.dev.java.net/what-is.html
44   * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
45   */
46  public class RestServlet2
47      extends HttpServlet
48  {
49      /**
50       * {@inheritDoc}
51       * 
52       * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
53       * javax.servlet.http.HttpServletResponse)
54       */
55      protected void service( HttpServletRequest request, HttpServletResponse response )
56          throws ServletException, IOException
57      {
58          Object resource = Monitoring.getRepository();
59          try
60          {
61              resource = findResource( split( request.getPathInfo() ), resource );
62          }
63          catch ( Exception e )
64          {
65              response.sendError( HttpServletResponse.SC_NOT_FOUND );
66          }
67          if ( resource == null )
68          {
69              response.setStatus( HttpServletResponse.SC_NO_CONTENT );
70              return;
71          }
72  
73          Renderer renderer = getRenderer( MimeUtils.getMimeTypes( request ), response );
74  
75          // renderer.render( resource );
76  
77      }
78  
79      /**
80       * Find the best renderer according to the accept header (mime-type)
81       * 
82       * @param request HTTP request
83       * @param response HTTP response
84       * @return a renderer
85       * @throws IOException some error occured
86       */
87      protected Renderer getRenderer( List<String> mimeTypes, HttpServletResponse response )
88          throws IOException
89      {
90          // TODO Support alternate renderers
91          response.setContentType( "application/json" );
92          return new JSonRenderer( response.getWriter() );
93      }
94  
95      protected Object findResource( String path, Object root )
96          throws Exception
97      {
98          return findResource( split( path ), root );
99      }
100 
101     private String createMethod( String element, String accessor )
102     {
103         return accessor + Character.toUpperCase( element.charAt( 0 ) ) + element.substring( 1 );
104     }
105 
106     private List<String> split( String path )
107     {
108         if ( path.startsWith( "/" ) )
109         {
110             path = path.substring( 1 );
111         }
112         List<String> elements = new ArrayList<String>();
113         // Don't use String.split to support java 1.3 compatibility
114         StringTokenizer tokenizer = new StringTokenizer( path, "/" );
115         while ( tokenizer.hasMoreTokens() )
116         {
117             elements.add( tokenizer.nextToken() );
118         }
119         return elements;
120     }
121 
122     private Object findResource( List<String> path, Object root )
123         throws Exception
124     {
125         for ( Iterator<String> iterator = path.iterator(); iterator.hasNext(); )
126         {
127             String element = iterator.next();
128 
129             if ( root.getClass().isArray() )
130             {
131                 int index = Integer.parseInt( element );
132                 root = Array.get( root, index );
133                 continue;
134             }
135             if ( root instanceof List )
136             {
137                 int index = Integer.parseInt( element );
138                 root = ( (List) root ).get( index );
139                 continue;
140             }
141             if ( root instanceof Map )
142             {
143                 root = ( (Map) root ).get( element );
144                 continue;
145             }
146 
147             String get = createMethod( element, "get" );
148             try
149             {
150                 root = invokeMethod( root, iterator, get );
151             }
152             catch ( NoSuchMethodException e )
153             {
154                 // Is this a method execution request ?
155                 root = invokeMethod( root, iterator, element );
156             }
157 
158             if ( root == null && iterator.hasNext() )
159             {
160                 throw new UnsupportedOperationException( "Unknown path" );
161             }
162         }
163         return root;
164     }
165 
166     private Object invokeMethod( Object root, Iterator<String> next, String name )
167         throws Exception
168     {
169         Method method = findMethod( root, name );
170         Class[] types = method.getParameterTypes();
171         if ( types.length == 0 )
172         {
173             return method.invoke( root, null );
174         }
175         Object arg = convert( next.next(), types[0] );
176         return method.invoke( root, new Object[] { arg } );
177     }
178 
179     private final List<Class[]> parameterTypes = new ArrayList<Class[]>();
180     {
181         parameterTypes.add( new Class[0] );
182         parameterTypes.add( new Class[] { String.class } );
183         parameterTypes.add( new Class[] { String[].class } );
184         parameterTypes.add( new Class[] { Class.class } );
185         parameterTypes.add( new Class[] { Class[].class } );
186     }
187 
188     private Method findMethod( Object root, String name )
189         throws Exception
190     {
191         for ( Class[] parameterType : parameterTypes )
192         {
193             try
194             {
195                 return root.getClass().getMethod( name, parameterType );
196             }
197             catch ( NoSuchMethodException e )
198             {
199                 continue;
200             }
201         }
202         throw new NoSuchMethodException( "no " + name + " method on object " + root.getClass() );
203     }
204 
205     /**
206      * @param arg
207      * @param type expected type
208      * @return
209      */
210     private Object convert( String arg, Class type )
211     {
212         if ( type.equals( String.class ) )
213         {
214             return arg;
215         }
216         if ( type.equals( Class.class ) )
217         {
218             return Monitoring.getRepository().getFeatureManager().getFeature( arg );
219         }
220         if ( type.isArray() )
221         {
222             StringTokenizer tokenizer = new StringTokenizer( arg, "+" );
223             List args = new ArrayList();
224             while ( tokenizer.hasMoreTokens() )
225             {
226                 args.add( convert( tokenizer.nextToken(), type.getComponentType() ) );
227             }
228             return args.toArray( (Object[]) Array
229                 .newInstance( type.getComponentType(), args.size() ) );
230         }
231         return null;
232     }
233 
234     private Collection<Class> parseFeatures( String element )
235     {
236         PluginManager manager = Monitoring.getRepository().getFeatureManager();
237         Collection<Class> features = new ArrayList<Class>();
238         StringTokenizer tokenizer = new StringTokenizer( element, "+" );
239         while ( tokenizer.hasMoreTokens() )
240         {
241             Class feature = manager.getFeature( tokenizer.nextToken() );
242             if ( feature != null )
243             {
244                 features.add( feature );
245             }
246         }
247         return features;
248     }
249 }