src/java/de/uapcore/lightpit/AbstractLightPITServlet.java

changeset 12
005d27918b57
parent 11
737ab27e37b3
child 13
f4608ad6c947
equal deleted inserted replaced
11:737ab27e37b3 12:005d27918b57
32 import java.lang.reflect.Method; 32 import java.lang.reflect.Method;
33 import java.lang.reflect.Modifier; 33 import java.lang.reflect.Modifier;
34 import java.util.HashMap; 34 import java.util.HashMap;
35 import java.util.Map; 35 import java.util.Map;
36 import java.util.Optional; 36 import java.util.Optional;
37 import java.util.function.BiConsumer;
38 import javax.servlet.ServletException; 37 import javax.servlet.ServletException;
39 import javax.servlet.http.HttpServlet; 38 import javax.servlet.http.HttpServlet;
40 import javax.servlet.http.HttpServletRequest; 39 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse; 40 import javax.servlet.http.HttpServletResponse;
42 import org.slf4j.Logger; 41 import org.slf4j.Logger;
58 /** 57 /**
59 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties. 58 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties.
60 */ 59 */
61 private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty(); 60 private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty();
62 61
62
63 @FunctionalInterface
64 private static interface HandlerMethod {
65 ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException, ServletException;
66 }
67
63 /** 68 /**
64 * Invocation mapping gathered from the {@link RequestMapping} annotations. 69 * Invocation mapping gathered from the {@link RequestMapping} annotations.
65 */ 70 */
66 private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>(); 71 private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
67 72
68 /** 73 /**
69 * Gives implementing modules access to the {@link ModuleManager}. 74 * Gives implementing modules access to the {@link ModuleManager}.
70 * @return the module manager 75 * @return the module manager
71 */ 76 */
72 protected final ModuleManager getModuleManager() { 77 protected final ModuleManager getModuleManager() {
73 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 78 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
74 } 79 }
75 80
76 private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) { 81 private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp)
82 throws IOException, ServletException {
77 try { 83 try {
78 LOG.debug("invoke {}", method.getName()); 84 LOG.debug("invoke {}", method.getName());
79 method.invoke(this, req, resp); 85 return (ResponseType) method.invoke(this, req, resp);
80 } catch (ReflectiveOperationException ex) { 86 } catch (ReflectiveOperationException | ClassCastException ex) {
81 LOG.error(String.format("invocation of method %s failed", method.getName()), ex); 87 LOG.error(String.format("invocation of method %s failed", method.getName()), ex);
88 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
89 return ResponseType.NONE;
82 } 90 }
83 } 91 }
84 92
85 @Override 93 @Override
86 public void init() throws ServletException { 94 public void init() throws ServletException {
87 moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class)); 95 moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class));
88 moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert); 96 moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert);
89 97
90 if (moduleInfo.isPresent()) { 98 if (moduleInfo.isPresent()) {
99 scanForRequestMappings();
100 }
101
102 LOG.trace("{} initialized", getServletName());
103 }
104
105 private void scanForRequestMappings() {
106 try {
91 Method[] methods = getClass().getDeclaredMethods(); 107 Method[] methods = getClass().getDeclaredMethods();
92 for (Method method : methods) { 108 for (Method method : methods) {
93 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class)); 109 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
94 if (mapping.isPresent()) { 110 if (mapping.isPresent()) {
95 if (!Modifier.isPublic(method.getModifiers())) { 111 if (!Modifier.isPublic(method.getModifiers())) {
102 LOG.warn("{} is annotated with {} but is abstract", 118 LOG.warn("{} is annotated with {} but is abstract",
103 method.getName(), RequestMapping.class.getSimpleName() 119 method.getName(), RequestMapping.class.getSimpleName()
104 ); 120 );
105 continue; 121 continue;
106 } 122 }
107 123 if (!ResponseType.class.isAssignableFrom(method.getReturnType())) {
124 LOG.warn("{} is annotated with {} but has the wrong return type - 'ResponseType' required",
125 method.getName(), RequestMapping.class.getSimpleName()
126 );
127 continue;
128 }
129
108 Class<?>[] params = method.getParameterTypes(); 130 Class<?>[] params = method.getParameterTypes();
109 if (params.length == 2 131 if (params.length == 2
110 && HttpServletRequest.class.isAssignableFrom(params[0]) 132 && HttpServletRequest.class.isAssignableFrom(params[0])
111 && HttpServletResponse.class.isAssignableFrom(params[1])) { 133 && HttpServletResponse.class.isAssignableFrom(params[1])) {
112 134
113 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 135 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
114 putIfAbsent(mapping.get().requestPath(), 136 putIfAbsent(mapping.get().requestPath(),
115 (req, resp) -> invokeMapping(method, req, resp)) != null) { 137 (req, resp) -> invokeMapping(method, req, resp)) != null) {
116 LOG.warn("{} {} has multiple mappings", 138 LOG.warn("{} {} has multiple mappings",
117 mapping.get().method(), 139 mapping.get().method(),
118 mapping.get().requestPath() 140 mapping.get().requestPath()
119 ); 141 );
120 } 142 }
121 143
122 LOG.info("{} {} maps to {}", 144 LOG.info("{} {} maps to {}",
123 mapping.get().method(), 145 mapping.get().method(),
124 mapping.get().requestPath(), 146 mapping.get().requestPath(),
125 method.getName() 147 method.getName()
126 ); 148 );
127 } else { 149 } else {
128 LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required", 150 LOG.warn("{} is annotated with {} but has the wrong parameters - (HttpServletRequest,HttpServletResponse) required",
129 method.getName(), RequestMapping.class.getSimpleName() 151 method.getName(), RequestMapping.class.getSimpleName()
130 ); 152 );
131 } 153 }
132 } 154 }
133 } 155 }
134 } 156 } catch (SecurityException ex) {
135 157 LOG.error("Scan for request mappings on declared methods failed.", ex);
136 LOG.trace("{} initialized", getServletName()); 158 }
137 } 159 }
138 160
139 @Override 161 @Override
140 public void destroy() { 162 public void destroy() {
141 mappings.clear(); 163 mappings.clear();
165 setGenericRequestAttributes(req); 187 setGenericRequestAttributes(req);
166 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); 188 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu());
167 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); 189 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp);
168 } 190 }
169 191
170 private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) { 192 private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
171 return Optional.ofNullable(mappings.get(method)).map( 193 return Optional.ofNullable(mappings.get(method)).map(
172 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("")) 194 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
173 ); 195 );
174 } 196 }
175 197
198 private void forwardAsSepcified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)
199 throws ServletException, IOException {
200 switch (type) {
201 case NONE: return;
202 case HTML_FULL:
203 forwardToFullView(req, resp);
204 return;
205 // TODO: implement remaining response types
206 default:
207 // this code should be unreachable
208 LOG.error("ResponseType switch is not exhaustive - this is a bug!");
209 throw new UnsupportedOperationException();
210 }
211 }
212
213 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp)
214 throws ServletException, IOException {
215 Optional<HandlerMethod> mapping = findMapping(method, req);
216 if (mapping.isPresent()) {
217 forwardAsSepcified(mapping.get().apply(req, resp), req, resp);
218 } else {
219 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
220 }
221 }
222
176 @Override 223 @Override
177 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) 224 protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
178 throws ServletException, IOException { 225 throws ServletException, IOException {
179 226 doProcess(HttpMethod.GET, req, resp);
180 findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp));
181
182 // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction)
183 // TODO: we should call a default handler, if no specific mapping could be found
184 forwardToFullView(req, resp);
185 } 227 }
186 228
187 @Override 229 @Override
188 protected final void doPost(HttpServletRequest req, HttpServletResponse resp) 230 protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
189 throws ServletException, IOException { 231 throws ServletException, IOException {
190 232 doProcess(HttpMethod.POST, req, resp);
191 findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp));
192
193 forwardToFullView(req, resp);
194 } 233 }
195 } 234 }

mercurial