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

changeset 11
737ab27e37b3
parent 10
89e3e6e28b69
child 12
005d27918b57
equal deleted inserted replaced
10:89e3e6e28b69 11:737ab27e37b3
27 * 27 *
28 */ 28 */
29 package de.uapcore.lightpit; 29 package de.uapcore.lightpit;
30 30
31 import java.io.IOException; 31 import java.io.IOException;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Modifier;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.function.BiConsumer;
32 import javax.servlet.ServletException; 38 import javax.servlet.ServletException;
33 import javax.servlet.http.HttpServlet; 39 import javax.servlet.http.HttpServlet;
34 import javax.servlet.http.HttpServletRequest; 40 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse; 41 import javax.servlet.http.HttpServletResponse;
36 import org.slf4j.Logger; 42 import org.slf4j.Logger;
41 * the necessary functionality for {@link LightPITModule}s. 47 * the necessary functionality for {@link LightPITModule}s.
42 */ 48 */
43 public abstract class AbstractLightPITServlet extends HttpServlet { 49 public abstract class AbstractLightPITServlet extends HttpServlet {
44 50
45 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); 51 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class);
52
53 /**
54 * Store a reference to the annotation for quicker access.
55 */
56 private Optional<LightPITModule> moduleInfo = Optional.empty();
46 57
58 /**
59 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties.
60 */
61 private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty();
47 62
63 /**
64 * Invocation mapping gathered from the {@link RequestMapping} annotations.
65 */
66 private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>();
67
48 /** 68 /**
49 * Gives implementing modules access to the {@link ModuleManager}. 69 * Gives implementing modules access to the {@link ModuleManager}.
50 * @return the module manager 70 * @return the module manager
51 */ 71 */
52 protected final ModuleManager getModuleManager() { 72 protected final ModuleManager getModuleManager() {
53 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 73 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
54 } 74 }
55 75
56 private void addPathInformation(HttpServletRequest req) { 76 private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) {
57 final String path = req.getServletPath()+"/"+req.getPathInfo(); 77 try {
58 req.setAttribute(Constants.REQ_ATTR_PATH, path); 78 LOG.debug("invoke {}", method.getName());
79 method.invoke(this, req, resp);
80 } catch (ReflectiveOperationException ex) {
81 LOG.error(String.format("invocation of method %s failed", method.getName()), ex);
82 }
83 }
84
85 @Override
86 public void init() throws ServletException {
87 moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class));
88 moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert);
89
90 if (moduleInfo.isPresent()) {
91 Method[] methods = getClass().getDeclaredMethods();
92 for (Method method : methods) {
93 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
94 if (mapping.isPresent()) {
95 if (!Modifier.isPublic(method.getModifiers())) {
96 LOG.warn("{} is annotated with {} but is not public",
97 method.getName(), RequestMapping.class.getSimpleName()
98 );
99 continue;
100 }
101 if (Modifier.isAbstract(method.getModifiers())) {
102 LOG.warn("{} is annotated with {} but is abstract",
103 method.getName(), RequestMapping.class.getSimpleName()
104 );
105 continue;
106 }
107
108 Class<?>[] params = method.getParameterTypes();
109 if (params.length == 2
110 && HttpServletRequest.class.isAssignableFrom(params[0])
111 && HttpServletResponse.class.isAssignableFrom(params[1])) {
112
113 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
114 putIfAbsent(mapping.get().requestPath(),
115 (req, resp) -> invokeMapping(method, req, resp)) != null) {
116 LOG.warn("{} {} has multiple mappings",
117 mapping.get().method(),
118 mapping.get().requestPath()
119 );
120 }
121
122 LOG.info("{} {} maps to {}",
123 mapping.get().method(),
124 mapping.get().requestPath(),
125 method.getName()
126 );
127 } else {
128 LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required",
129 method.getName(), RequestMapping.class.getSimpleName()
130 );
131 }
132 }
133 }
134 }
135
136 LOG.trace("{} initialized", getServletName());
137 }
138
139 @Override
140 public void destroy() {
141 mappings.clear();
142 LOG.trace("{} destroyed", getServletName());
143 }
144
145
146 /**
147 * Sets several requests attributes, that can be used by the JSP.
148 *
149 * @param req the servlet request object
150 * @see Constants#REQ_ATTR_PATH
151 * @see Constants#REQ_ATTR_MODULE_CLASSNAME
152 * @see Constants#REQ_ATTR_MODULE_INFO
153 */
154 private void setGenericRequestAttributes(HttpServletRequest req) {
155 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req));
156
157 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName());
158
159 moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy));
59 } 160 }
60 161
61 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) 162 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp)
62 throws IOException, ServletException { 163 throws IOException, ServletException {
63 164
64 addPathInformation(req); 165 setGenericRequestAttributes(req);
65 166 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu());
66 final ModuleManager mm = getModuleManager();
67 req.setAttribute(Constants.REQ_ATTR_MENU, mm.getMainMenu());
68
69 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); 167 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp);
168 }
169
170 private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) {
171 return Optional.ofNullable(mappings.get(method)).map(
172 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
173 );
70 } 174 }
71 175
72 @Override 176 @Override
73 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) 177 protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
74 throws ServletException, IOException { 178 throws ServletException, IOException {
179
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
75 forwardToFullView(req, resp); 184 forwardToFullView(req, resp);
76 } 185 }
77 186
78 @Override 187 @Override
79 protected final void doPost(HttpServletRequest req, HttpServletResponse resp) 188 protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
80 throws ServletException, IOException { 189 throws ServletException, IOException {
81 190
191 findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp));
192
82 forwardToFullView(req, resp); 193 forwardToFullView(req, resp);
83 } 194 }
84 } 195 }

mercurial