1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody.internal.web;
19
20 import java.io.IOException;
21 import java.lang.annotation.Annotation;
22 import java.lang.annotation.ElementType;
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.lang.annotation.Target;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.HashMap;
29 import java.util.Map;
30
31 import javax.servlet.http.HttpServletRequest;
32
33 import net.bull.javamelody.internal.common.HttpParameter;
34 import net.bull.javamelody.internal.common.HttpPart;
35
36 /**
37  * Maps of http requests to methods of controllers, based on request parameters and annotations on methods and parameters.
38  * @param <T> Type of the controller having methods annotated with @HttpPart
39  * @author Emeric Vernat
40  */

41 class RequestToMethodMapper<T> {
42     private final Map<HttpPart, Method> methodsByPart = new HashMap<>();
43
44     @Target(ElementType.METHOD)
45     @Retention(RetentionPolicy.RUNTIME)
46     @interface RequestPart {
47         /**
48          * @return String
49          */

50         HttpPart value();
51     }
52
53     @Target(ElementType.PARAMETER)
54     @Retention(RetentionPolicy.RUNTIME)
55     @interface RequestParameter {
56         /**
57          * @return String
58          */

59         HttpParameter value();
60     }
61
62     @Target(ElementType.PARAMETER)
63     @Retention(RetentionPolicy.RUNTIME)
64     @interface RequestAttribute {
65         /**
66          * @return String
67          */

68         String value();
69     }
70
71     @Target(ElementType.PARAMETER)
72     @Retention(RetentionPolicy.RUNTIME)
73     @interface RequestHeader {
74         /**
75          * @return String
76          */

77         String value();
78     }
79
80     RequestToMethodMapper(Class<T> clazz) {
81         super();
82         for (final Method method : clazz.getDeclaredMethods()) {
83             final RequestPart partAnnotation = method.getAnnotation(RequestPart.class);
84             if (partAnnotation != null) {
85                 methodsByPart.put(partAnnotation.value(), method);
86             }
87         }
88     }
89
90     void invoke(HttpServletRequest httpRequest, T controller) throws IOException {
91         invokeAndReturn(httpRequest, controller);
92     }
93
94     Object invokeAndReturn(HttpServletRequest httpRequest, T controller) throws IOException {
95         final String partParameter = HttpParameter.PART.getParameterFrom(httpRequest);
96         final HttpPart httpPart = HttpPart.getByName(partParameter);
97         final Method method = methodsByPart.get(httpPart);
98         if (method == null) {
99             throw new IllegalArgumentException("Unknown http part: " + partParameter);
100         }
101         try {
102             // find parameters values
103             final Object[] parameterValues = getParameterValues(httpRequest, method);
104             // invoke the method (the "endpoint")
105             return method.invoke(controller, parameterValues);
106         } catch (final IllegalAccessException e) {
107             throw new IllegalStateException(e);
108         } catch (final InvocationTargetException e) {
109             final Throwable targetException = e.getTargetException();
110             if (targetException instanceof IOException) {
111                 throw (IOException) targetException;
112             } else if (targetException instanceof RuntimeException) {
113                 throw (RuntimeException) targetException;
114             } else if (targetException instanceof Error) {
115                 throw (Error) targetException;
116             }
117             throw new IOException(targetException);
118         }
119     }
120
121     private Object[] getParameterValues(HttpServletRequest request, Method method) {
122         // note: method.getParameters() existe seulement depuis Java 8
123         final Annotation[][] parameters = method.getParameterAnnotations();
124         final Object[] values = new Object[parameters.length];
125         for (int i = 0; i < parameters.length; i++) {
126             final Annotation[] parameter = parameters[i];
127             boolean found = false;
128             for (final Annotation annotation : parameter) {
129                 if (annotation.annotationType() == RequestParameter.class) {
130                     final HttpParameter requestParameter = ((RequestParameter) annotation).value();
131                     values[i] = requestParameter.getParameterFrom(request);
132                     found = true;
133                     break;
134                 } else if (annotation.annotationType() == RequestAttribute.class) {
135                     final String requestAttribute = ((RequestAttribute) annotation).value();
136                     values[i] = request.getAttribute(requestAttribute);
137                     found = true;
138                     break;
139                 } else if (annotation.annotationType() == RequestHeader.class) {
140                     final String requestHeader = ((RequestHeader) annotation).value();
141                     values[i] = request.getHeader(requestHeader);
142                     found = true;
143                     break;
144                 }
145             }
146             if (!found) {
147                 throw new IllegalStateException(
148                         "a parameter not annotated in method " + method.getName());
149             }
150         }
151         return values;
152     }
153 }
154
by