Sun, 17 Dec 2017 01:45:28 +0100
implements simple request mapper
1.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Dec 16 20:19:28 2017 +0100 1.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sun Dec 17 01:45:28 2017 +0100 1.3 @@ -29,6 +29,12 @@ 1.4 package de.uapcore.lightpit; 1.5 1.6 import java.io.IOException; 1.7 +import java.lang.reflect.Method; 1.8 +import java.lang.reflect.Modifier; 1.9 +import java.util.HashMap; 1.10 +import java.util.Map; 1.11 +import java.util.Optional; 1.12 +import java.util.function.BiConsumer; 1.13 import javax.servlet.ServletException; 1.14 import javax.servlet.http.HttpServlet; 1.15 import javax.servlet.http.HttpServletRequest; 1.16 @@ -43,9 +49,23 @@ 1.17 public abstract class AbstractLightPITServlet extends HttpServlet { 1.18 1.19 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); 1.20 + 1.21 + /** 1.22 + * Store a reference to the annotation for quicker access. 1.23 + */ 1.24 + private Optional<LightPITModule> moduleInfo = Optional.empty(); 1.25 1.26 + /** 1.27 + * The EL proxy is necessary, because the EL resolver cannot handle annotation properties. 1.28 + */ 1.29 + private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty(); 1.30 1.31 /** 1.32 + * Invocation mapping gathered from the {@link RequestMapping} annotations. 1.33 + */ 1.34 + private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>(); 1.35 + 1.36 + /** 1.37 * Gives implementing modules access to the {@link ModuleManager}. 1.38 * @return the module manager 1.39 */ 1.40 @@ -53,25 +73,114 @@ 1.41 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 1.42 } 1.43 1.44 - private void addPathInformation(HttpServletRequest req) { 1.45 - final String path = req.getServletPath()+"/"+req.getPathInfo(); 1.46 - req.setAttribute(Constants.REQ_ATTR_PATH, path); 1.47 + private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) { 1.48 + try { 1.49 + LOG.debug("invoke {}", method.getName()); 1.50 + method.invoke(this, req, resp); 1.51 + } catch (ReflectiveOperationException ex) { 1.52 + LOG.error(String.format("invocation of method %s failed", method.getName()), ex); 1.53 + } 1.54 + } 1.55 + 1.56 + @Override 1.57 + public void init() throws ServletException { 1.58 + moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class)); 1.59 + moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert); 1.60 + 1.61 + if (moduleInfo.isPresent()) { 1.62 + Method[] methods = getClass().getDeclaredMethods(); 1.63 + for (Method method : methods) { 1.64 + Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class)); 1.65 + if (mapping.isPresent()) { 1.66 + if (!Modifier.isPublic(method.getModifiers())) { 1.67 + LOG.warn("{} is annotated with {} but is not public", 1.68 + method.getName(), RequestMapping.class.getSimpleName() 1.69 + ); 1.70 + continue; 1.71 + } 1.72 + if (Modifier.isAbstract(method.getModifiers())) { 1.73 + LOG.warn("{} is annotated with {} but is abstract", 1.74 + method.getName(), RequestMapping.class.getSimpleName() 1.75 + ); 1.76 + continue; 1.77 + } 1.78 + 1.79 + Class<?>[] params = method.getParameterTypes(); 1.80 + if (params.length == 2 1.81 + && HttpServletRequest.class.isAssignableFrom(params[0]) 1.82 + && HttpServletResponse.class.isAssignableFrom(params[1])) { 1.83 + 1.84 + if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 1.85 + putIfAbsent(mapping.get().requestPath(), 1.86 + (req, resp) -> invokeMapping(method, req, resp)) != null) { 1.87 + LOG.warn("{} {} has multiple mappings", 1.88 + mapping.get().method(), 1.89 + mapping.get().requestPath() 1.90 + ); 1.91 + } 1.92 + 1.93 + LOG.info("{} {} maps to {}", 1.94 + mapping.get().method(), 1.95 + mapping.get().requestPath(), 1.96 + method.getName() 1.97 + ); 1.98 + } else { 1.99 + LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required", 1.100 + method.getName(), RequestMapping.class.getSimpleName() 1.101 + ); 1.102 + } 1.103 + } 1.104 + } 1.105 + } 1.106 + 1.107 + LOG.trace("{} initialized", getServletName()); 1.108 + } 1.109 + 1.110 + @Override 1.111 + public void destroy() { 1.112 + mappings.clear(); 1.113 + LOG.trace("{} destroyed", getServletName()); 1.114 + } 1.115 + 1.116 + 1.117 + /** 1.118 + * Sets several requests attributes, that can be used by the JSP. 1.119 + * 1.120 + * @param req the servlet request object 1.121 + * @see Constants#REQ_ATTR_PATH 1.122 + * @see Constants#REQ_ATTR_MODULE_CLASSNAME 1.123 + * @see Constants#REQ_ATTR_MODULE_INFO 1.124 + */ 1.125 + private void setGenericRequestAttributes(HttpServletRequest req) { 1.126 + req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); 1.127 + 1.128 + req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 1.129 + 1.130 + moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 1.131 } 1.132 1.133 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) 1.134 throws IOException, ServletException { 1.135 1.136 - addPathInformation(req); 1.137 - 1.138 - final ModuleManager mm = getModuleManager(); 1.139 - req.setAttribute(Constants.REQ_ATTR_MENU, mm.getMainMenu()); 1.140 - 1.141 + setGenericRequestAttributes(req); 1.142 + req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); 1.143 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); 1.144 } 1.145 1.146 + private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) { 1.147 + return Optional.ofNullable(mappings.get(method)).map( 1.148 + (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("")) 1.149 + ); 1.150 + } 1.151 + 1.152 @Override 1.153 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) 1.154 throws ServletException, IOException { 1.155 + 1.156 + findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp)); 1.157 + 1.158 + // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction) 1.159 + // TODO: we should call a default handler, if no specific mapping could be found 1.160 forwardToFullView(req, resp); 1.161 } 1.162 1.163 @@ -79,6 +188,8 @@ 1.164 protected final void doPost(HttpServletRequest req, HttpServletResponse resp) 1.165 throws ServletException, IOException { 1.166 1.167 + findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp)); 1.168 + 1.169 forwardToFullView(req, resp); 1.170 } 1.171 }
2.1 --- a/src/java/de/uapcore/lightpit/Constants.java Sat Dec 16 20:19:28 2017 +0100 2.2 +++ b/src/java/de/uapcore/lightpit/Constants.java Sun Dec 17 01:45:28 2017 +0100 2.3 @@ -28,16 +28,33 @@ 2.4 */ 2.5 package de.uapcore.lightpit; 2.6 2.7 -import static de.uapcore.lightpit.Functions.fullyQualifiedName; 2.8 +import static de.uapcore.lightpit.Functions.fqn; 2.9 2.10 /** 2.11 * Contains all constants used by the this application. 2.12 */ 2.13 public final class Constants { 2.14 - public static final String JSP_PATH_PREFIX = "/WEB-INF/view/"; 2.15 + public static final String JSP_PATH_PREFIX = "/WEB-INF/jsp/"; 2.16 2.17 - public static final String REQ_ATTR_MENU = fullyQualifiedName(AbstractLightPITServlet.class, "mainMenu"); 2.18 - public static final String REQ_ATTR_PATH = fullyQualifiedName(AbstractLightPITServlet.class, "path"); 2.19 + /** 2.20 + * Key for the request attribute containing the class name of the currently dispatching module. 2.21 + */ 2.22 + public static final String REQ_ATTR_MODULE_CLASSNAME = fqn(AbstractLightPITServlet.class, "moduleClassname"); 2.23 + 2.24 + /** 2.25 + * Key for the request attribute containing the {@link LightPITModule} information of the currently dispatching module. 2.26 + */ 2.27 + public static final String REQ_ATTR_MODULE_INFO = fqn(AbstractLightPITServlet.class, "moduleInfo"); 2.28 + 2.29 + /** 2.30 + * Key for the request attribute containing the menu list. 2.31 + */ 2.32 + public static final String REQ_ATTR_MENU = fqn(AbstractLightPITServlet.class, "mainMenu"); 2.33 + 2.34 + /** 2.35 + * Key for the request attribute containing the full path information (servlet path + path info). 2.36 + */ 2.37 + public static final String REQ_ATTR_PATH = fqn(AbstractLightPITServlet.class, "path"); 2.38 2.39 /** 2.40 * This class is not instantiatable.
3.1 --- a/src/java/de/uapcore/lightpit/Functions.java Sat Dec 16 20:19:28 2017 +0100 3.2 +++ b/src/java/de/uapcore/lightpit/Functions.java Sun Dec 17 01:45:28 2017 +0100 3.3 @@ -28,6 +28,8 @@ 3.4 */ 3.5 package de.uapcore.lightpit; 3.6 3.7 +import java.util.Optional; 3.8 +import javax.servlet.http.HttpServletRequest; 3.9 import org.slf4j.Logger; 3.10 import org.slf4j.LoggerFactory; 3.11 3.12 @@ -42,12 +44,27 @@ 3.13 return Constants.JSP_PATH_PREFIX + filename; 3.14 } 3.15 3.16 - public static String fullyQualifiedName(String base, String name) { 3.17 + public static String fqn(String base, String name) { 3.18 return base+"."+name; 3.19 } 3.20 3.21 - public static String fullyQualifiedName(Class clazz, String name) { 3.22 - return fullyQualifiedName(clazz.getName(), name); 3.23 + public static String fqn(Class clazz, String name) { 3.24 + return fqn(clazz.getName(), name); 3.25 + } 3.26 + 3.27 + public static String fullPath(LightPITModule module, RequestMapping mapping) { 3.28 + StringBuilder sb = new StringBuilder(); 3.29 + sb.append(module.modulePath()); 3.30 + sb.append('/'); 3.31 + if (!mapping.requestPath().isEmpty()) { 3.32 + sb.append(mapping.requestPath().isEmpty()); 3.33 + sb.append('/'); 3.34 + } 3.35 + return sb.toString(); 3.36 + } 3.37 + 3.38 + public static String fullPath(HttpServletRequest req) { 3.39 + return req.getServletPath() + Optional.ofNullable(req.getPathInfo()).orElse(""); 3.40 } 3.41 3.42 /** 3.43 @@ -60,15 +77,15 @@ 3.44 * @return the module path 3.45 */ 3.46 public static String modulePathOf(Class<? extends AbstractLightPITServlet> clazz) { 3.47 - LightPITModule moduleInfo = clazz.getAnnotation(LightPITModule.class); 3.48 - if (moduleInfo == null) { 3.49 + Optional<LightPITModule> moduleInfo = Optional.ofNullable(clazz.getAnnotation(LightPITModule.class)); 3.50 + if (moduleInfo.isPresent()) { 3.51 + return moduleInfo.get().modulePath(); 3.52 + } else { 3.53 LOG.warn( 3.54 "{} is a LightPIT Servlet but is missing the module annotation.", 3.55 clazz.getName() 3.56 ); 3.57 return "/error/404.html"; 3.58 - } else { 3.59 - return moduleInfo.modulePath(); 3.60 } 3.61 } 3.62
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/java/de/uapcore/lightpit/HttpMethod.java Sun Dec 17 01:45:28 2017 +0100 4.3 @@ -0,0 +1,34 @@ 4.4 +/* 4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 4.6 + * 4.7 + * Copyright 2017 Mike Becker. All rights reserved. 4.8 + * 4.9 + * Redistribution and use in source and binary forms, with or without 4.10 + * modification, are permitted provided that the following conditions are met: 4.11 + * 4.12 + * 1. Redistributions of source code must retain the above copyright 4.13 + * notice, this list of conditions and the following disclaimer. 4.14 + * 4.15 + * 2. Redistributions in binary form must reproduce the above copyright 4.16 + * notice, this list of conditions and the following disclaimer in the 4.17 + * documentation and/or other materials provided with the distribution. 4.18 + * 4.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 4.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 4.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 4.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 4.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 4.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 4.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 4.29 + * POSSIBILITY OF SUCH DAMAGE. 4.30 + * 4.31 + */ 4.32 +package de.uapcore.lightpit; 4.33 + 4.34 + 4.35 +public enum HttpMethod { 4.36 + GET, POST, PUT, DELETE, TRACE, HEAD, OPTIONS 4.37 +}
5.1 --- a/src/java/de/uapcore/lightpit/LightPITModule.java Sat Dec 16 20:19:28 2017 +0100 5.2 +++ b/src/java/de/uapcore/lightpit/LightPITModule.java Sun Dec 17 01:45:28 2017 +0100 5.3 @@ -71,7 +71,60 @@ 5.4 5.5 /** 5.6 * Returns the properties key for the menu label. 5.7 - * @return the properties key relative to the base name 5.8 + * @return the properties key 5.9 */ 5.10 String menuKey() default "menuLabel"; 5.11 + 5.12 + /** 5.13 + * Returns the properties key for the page title. 5.14 + * 5.15 + * By default this is the same as the menu label. 5.16 + * 5.17 + * @return the properties key 5.18 + */ 5.19 + String titleKey() default "menuLabel"; 5.20 + 5.21 + /** 5.22 + * Class representing the annotation. 5.23 + * This is necessary, because the EL resolver cannot deal with 5.24 + * annotation objects. 5.25 + * 5.26 + * Note, that only the properties which are interesting for the JSP pages 5.27 + * are proxied by this object. 5.28 + */ 5.29 + public static class ELProxy { 5.30 + private final String bundleBaseName, modulePath, menuKey, titleKey; 5.31 + 5.32 + public static ELProxy convert(LightPITModule annotation) { 5.33 + return new ELProxy( 5.34 + annotation.bundleBaseName(), 5.35 + annotation.modulePath(), 5.36 + annotation.menuKey(), 5.37 + annotation.titleKey() 5.38 + ); 5.39 + } 5.40 + 5.41 + private ELProxy(String bundleBaseName, String modulePath, String menuKey, String titleKey) { 5.42 + this.bundleBaseName = bundleBaseName; 5.43 + this.modulePath = modulePath; 5.44 + this.menuKey = menuKey; 5.45 + this.titleKey = titleKey; 5.46 + } 5.47 + 5.48 + public String getBundleBaseName() { 5.49 + return bundleBaseName; 5.50 + } 5.51 + 5.52 + public String getMenuKey() { 5.53 + return menuKey; 5.54 + } 5.55 + 5.56 + public String getModulePath() { 5.57 + return modulePath; 5.58 + } 5.59 + 5.60 + public String getTitleKey() { 5.61 + return titleKey; 5.62 + } 5.63 + } 5.64 }
6.1 --- a/src/java/de/uapcore/lightpit/Menu.java Sat Dec 16 20:19:28 2017 +0100 6.2 +++ b/src/java/de/uapcore/lightpit/Menu.java Sun Dec 17 01:45:28 2017 +0100 6.3 @@ -44,12 +44,27 @@ 6.4 private final List<MenuEntry> entries = new ArrayList<>(); 6.5 private final List<MenuEntry> immutableEntries = Collections.unmodifiableList(entries); 6.6 6.7 + /** 6.8 + * Class name of the module for which this menu is built. 6.9 + */ 6.10 + private String moduleClassName; 6.11 + 6.12 + 6.13 public Menu() { 6.14 super(); 6.15 } 6.16 6.17 - public Menu(ResourceKey resourceKey, String pathName) { 6.18 + public Menu(String moduleClassName, ResourceKey resourceKey, String pathName) { 6.19 super(resourceKey, pathName); 6.20 + this.moduleClassName = moduleClassName; 6.21 + } 6.22 + 6.23 + public void setModuleClassName(String moduleClassName) { 6.24 + this.moduleClassName = moduleClassName; 6.25 + } 6.26 + 6.27 + public String getModuleClassName() { 6.28 + return moduleClassName; 6.29 } 6.30 6.31 /**
7.1 --- a/src/java/de/uapcore/lightpit/MenuEntry.java Sat Dec 16 20:19:28 2017 +0100 7.2 +++ b/src/java/de/uapcore/lightpit/MenuEntry.java Sun Dec 17 01:45:28 2017 +0100 7.3 @@ -37,7 +37,14 @@ 7.4 */ 7.5 public class MenuEntry { 7.6 7.7 + /** 7.8 + * Resource key for the menu label. 7.9 + */ 7.10 private ResourceKey resourceKey; 7.11 + 7.12 + /** 7.13 + * Path name of the module, linked by this menu entry. 7.14 + */ 7.15 private String pathName; 7.16 7.17 public MenuEntry() { 7.18 @@ -48,38 +55,18 @@ 7.19 this.pathName = pathName; 7.20 } 7.21 7.22 - /** 7.23 - * Sets the resource key, which is used to look up the menu label. 7.24 - * 7.25 - * @param resourceKey the key for the resource bundle 7.26 - */ 7.27 public void setResourceKey(ResourceKey resourceKey) { 7.28 this.resourceKey = resourceKey; 7.29 } 7.30 7.31 - /** 7.32 - * Retrieves the resource key. 7.33 - * 7.34 - * @return the key for the resource bundle 7.35 - */ 7.36 public ResourceKey getResourceKey() { 7.37 return resourceKey; 7.38 } 7.39 7.40 - /** 7.41 - * Sets the path name of the web page accessed via this menu entry. 7.42 - * 7.43 - * @param pathName path name relative to the context path 7.44 - */ 7.45 public void setPathName(String pathName) { 7.46 this.pathName = pathName; 7.47 } 7.48 7.49 - /** 7.50 - * Retrieves the path name of the web page accessed via this menu entry. 7.51 - * 7.52 - * @return path name relative to the context path 7.53 - */ 7.54 public String getPathName() { 7.55 return pathName; 7.56 }
8.1 --- a/src/java/de/uapcore/lightpit/ModuleManager.java Sat Dec 16 20:19:28 2017 +0100 8.2 +++ b/src/java/de/uapcore/lightpit/ModuleManager.java Sun Dec 17 01:45:28 2017 +0100 8.3 @@ -107,8 +107,9 @@ 8.4 } 8.5 } 8.6 8.7 - private void addModuleToMenu(LightPITModule moduleInfo) { 8.8 + private void addModuleToMenu(String moduleClassName, LightPITModule moduleInfo) { 8.9 final Menu menu = new Menu( 8.10 + moduleClassName, 8.11 new ResourceKey(moduleInfo.bundleBaseName(), moduleInfo.menuKey()), 8.12 moduleInfo.modulePath() 8.13 ); 8.14 @@ -118,7 +119,9 @@ 8.15 private void handleServletRegistration(String name, Registration reg) { 8.16 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); 8.17 if (moduleInfo.isPresent()) { 8.18 - addModuleToMenu(moduleInfo.get()); 8.19 + 8.20 + // TODO: remove this call and add the module to some dependency resolver, first 8.21 + addModuleToMenu(reg.getClassName(), moduleInfo.get()); 8.22 8.23 LOG.info("Module detected: {}", name); 8.24 } else {
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/java/de/uapcore/lightpit/RequestMapping.java Sun Dec 17 01:45:28 2017 +0100 9.3 @@ -0,0 +1,76 @@ 9.4 +/* 9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 9.6 + * 9.7 + * Copyright 2017 Mike Becker. All rights reserved. 9.8 + * 9.9 + * Redistribution and use in source and binary forms, with or without 9.10 + * modification, are permitted provided that the following conditions are met: 9.11 + * 9.12 + * 1. Redistributions of source code must retain the above copyright 9.13 + * notice, this list of conditions and the following disclaimer. 9.14 + * 9.15 + * 2. Redistributions in binary form must reproduce the above copyright 9.16 + * notice, this list of conditions and the following disclaimer in the 9.17 + * documentation and/or other materials provided with the distribution. 9.18 + * 9.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 9.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 9.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 9.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 9.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 9.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 9.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 9.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 9.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 9.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 9.29 + * POSSIBILITY OF SUCH DAMAGE. 9.30 + * 9.31 + */ 9.32 +package de.uapcore.lightpit; 9.33 + 9.34 +import java.lang.annotation.Documented; 9.35 +import java.lang.annotation.ElementType; 9.36 +import java.lang.annotation.Retention; 9.37 +import java.lang.annotation.RetentionPolicy; 9.38 +import java.lang.annotation.Target; 9.39 + 9.40 + 9.41 +/** 9.42 + * Maps requests to methods. 9.43 + * 9.44 + * This annotation is used to annotate methods within classes which 9.45 + * override {@link AbstractLightPITServlet}. 9.46 + */ 9.47 +@Documented 9.48 +@Retention(RetentionPolicy.RUNTIME) 9.49 +@Target(ElementType.METHOD) 9.50 +public @interface RequestMapping { 9.51 + 9.52 + /** 9.53 + * Specifies the HTTP method. 9.54 + * 9.55 + * @return the HTTP method handled by the annotated Java method 9.56 + */ 9.57 + HttpMethod method(); 9.58 + 9.59 + /** 9.60 + * Specifies the request path relative to the module path. 9.61 + * 9.62 + * If a menu key is specified, this is also the path, which is linked 9.63 + * by the menu entry. 9.64 + * 9.65 + * The path must be specified <em>without</em> a trailing slash. 9.66 + * 9.67 + * @return the request path the annotated method should handle 9.68 + */ 9.69 + String requestPath() default ""; 9.70 + 9.71 + /** 9.72 + * Returns the properties key for the (sub) menu label. 9.73 + * 9.74 + * This should only be used for {@link HttpMethod#GET} requests. 9.75 + * 9.76 + * @return the properties key 9.77 + */ 9.78 + String menuKey() default ""; 9.79 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/java/de/uapcore/lightpit/modules/LanguageModule.java Sun Dec 17 01:45:28 2017 +0100 10.3 @@ -0,0 +1,54 @@ 10.4 +/* 10.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 10.6 + * 10.7 + * Copyright 2017 Mike Becker. All rights reserved. 10.8 + * 10.9 + * Redistribution and use in source and binary forms, with or without 10.10 + * modification, are permitted provided that the following conditions are met: 10.11 + * 10.12 + * 1. Redistributions of source code must retain the above copyright 10.13 + * notice, this list of conditions and the following disclaimer. 10.14 + * 10.15 + * 2. Redistributions in binary form must reproduce the above copyright 10.16 + * notice, this list of conditions and the following disclaimer in the 10.17 + * documentation and/or other materials provided with the distribution. 10.18 + * 10.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 10.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 10.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 10.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 10.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 10.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 10.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 10.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 10.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 10.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 10.29 + * POSSIBILITY OF SUCH DAMAGE. 10.30 + * 10.31 + */ 10.32 +package de.uapcore.lightpit.modules; 10.33 + 10.34 +import de.uapcore.lightpit.LightPITModule; 10.35 +import de.uapcore.lightpit.AbstractLightPITServlet; 10.36 +import de.uapcore.lightpit.HttpMethod; 10.37 +import javax.servlet.annotation.WebServlet; 10.38 +import javax.servlet.http.HttpServletRequest; 10.39 +import javax.servlet.http.HttpServletResponse; 10.40 +import de.uapcore.lightpit.RequestMapping; 10.41 + 10.42 + 10.43 +@LightPITModule( 10.44 + bundleBaseName = "de.uapcore.lightpit.resources.localization.language", 10.45 + modulePath = "language" 10.46 +) 10.47 +@WebServlet( 10.48 + name = "LanguageModule", 10.49 + urlPatterns = "/language/*" 10.50 +) 10.51 +public final class LanguageModule extends AbstractLightPITServlet { 10.52 + 10.53 + @RequestMapping(method = HttpMethod.GET) 10.54 + public void handle(HttpServletRequest req, HttpServletResponse resp) { 10.55 + 10.56 + } 10.57 +}
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/language.properties Sun Dec 17 01:45:28 2017 +0100 11.3 @@ -0,0 +1,24 @@ 11.4 +# Copyright 2017 Mike Becker. All rights reserved. 11.5 +# 11.6 +# Redistribution and use in source and binary forms, with or without 11.7 +# modification, are permitted provided that the following conditions are met: 11.8 +# 11.9 +# 1. Redistributions of source code must retain the above copyright 11.10 +# notice, this list of conditions and the following disclaimer. 11.11 +# 11.12 +# 2. Redistributions in binary form must reproduce the above copyright 11.13 +# notice, this list of conditions and the following disclaimer in the 11.14 +# documentation and/or other materials provided with the distribution. 11.15 +# 11.16 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 11.17 +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 11.18 +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11.19 +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 11.20 +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 11.21 +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 11.22 +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 11.23 +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 11.24 +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 11.25 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11.26 + 11.27 +menuLabel = Languages
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/language_de.properties Sun Dec 17 01:45:28 2017 +0100 12.3 @@ -0,0 +1,24 @@ 12.4 +# Copyright 2017 Mike Becker. All rights reserved. 12.5 +# 12.6 +# Redistribution and use in source and binary forms, with or without 12.7 +# modification, are permitted provided that the following conditions are met: 12.8 +# 12.9 +# 1. Redistributions of source code must retain the above copyright 12.10 +# notice, this list of conditions and the following disclaimer. 12.11 +# 12.12 +# 2. Redistributions in binary form must reproduce the above copyright 12.13 +# notice, this list of conditions and the following disclaimer in the 12.14 +# documentation and/or other materials provided with the distribution. 12.15 +# 12.16 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 12.17 +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12.18 +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 12.19 +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 12.20 +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 12.21 +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 12.22 +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 12.23 +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 12.24 +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 12.25 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12.26 + 12.27 +menuLabel = Sprache
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/web/WEB-INF/jsp/full.jsp Sun Dec 17 01:45:28 2017 +0100 13.3 @@ -0,0 +1,78 @@ 13.4 +<%-- 13.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 13.6 + 13.7 +Copyright 2017 Mike Becker. All rights reserved. 13.8 + 13.9 +Redistribution and use in source and binary forms, with or without 13.10 +modification, are permitted provided that the following conditions are met: 13.11 + 13.12 +1. Redistributions of source code must retain the above copyright 13.13 +notice, this list of conditions and the following disclaimer. 13.14 + 13.15 +2. Redistributions in binary form must reproduce the above copyright 13.16 +notice, this list of conditions and the following disclaimer in the 13.17 +documentation and/or other materials provided with the distribution. 13.18 + 13.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 13.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 13.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 13.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 13.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 13.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 13.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13.29 +--%> 13.30 +<%@page contentType="text/html" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %> 13.31 +<%@page import="de.uapcore.lightpit.Constants" %> 13.32 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 13.33 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 13.34 +<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 13.35 + 13.36 +<%-- Define an alias for the main menu --%> 13.37 +<c:set scope="page" var="mainMenu" value="${requestScope[Constants.REQ_ATTR_MENU]}"/> 13.38 + 13.39 +<%-- Define an alias for the module info --%> 13.40 +<c:set scope="page" var="moduleInfo" value="${requestScope[Constants.REQ_ATTR_MODULE_INFO]}"/> 13.41 + 13.42 +<!DOCTYPE html> 13.43 +<html> 13.44 + <head> 13.45 + <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"> 13.46 + <title>LightPIT - 13.47 + <fmt:bundle basename="${moduleInfo.bundleBaseName}"> 13.48 + <fmt:message key="${moduleInfo.titleKey}" /> 13.49 + </fmt:bundle> 13.50 + </title> 13.51 + <meta charset="UTF-8"> 13.52 + <link rel="stylesheet" href="lightpit.css" type="text/css"> 13.53 + </head> 13.54 + <body> 13.55 + <div id="mainMenu"> 13.56 + <c:forEach var="menu" items="${mainMenu}"> 13.57 + <div class="menuEntry" 13.58 + <c:if test="${requestScope[Constants.REQ_ATTR_MODULE_CLASSNAME] eq menu.moduleClassName}"> 13.59 + data-active 13.60 + </c:if> 13.61 + > 13.62 + <a href="${menu.pathName}"> 13.63 + <fmt:bundle basename="${menu.resourceKey.bundle}"> 13.64 + <fmt:message key="${menu.resourceKey.key}" /> 13.65 + </fmt:bundle> 13.66 + </a> 13.67 + </div> 13.68 + </c:forEach> 13.69 + 13.70 + </div> 13.71 + <div id="subMenu"> 13.72 + 13.73 + </div> 13.74 + <div id="content-area"> 13.75 + <%-- Resource keys should be rooted in the specific bundle for this module. --%> 13.76 + <fmt:bundle basename="${moduleInfo.bundleBaseName}"> 13.77 + TODO: load fragment 13.78 + </fmt:bundle> 13.79 + </div> 13.80 + </body> 13.81 +</html>
14.1 --- a/web/WEB-INF/view/full.jsp Sat Dec 16 20:19:28 2017 +0100 14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 14.3 @@ -1,69 +0,0 @@ 14.4 -<%-- 14.5 -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 14.6 - 14.7 -Copyright 2017 Mike Becker. All rights reserved. 14.8 - 14.9 -Redistribution and use in source and binary forms, with or without 14.10 -modification, are permitted provided that the following conditions are met: 14.11 - 14.12 -1. Redistributions of source code must retain the above copyright 14.13 -notice, this list of conditions and the following disclaimer. 14.14 - 14.15 -2. Redistributions in binary form must reproduce the above copyright 14.16 -notice, this list of conditions and the following disclaimer in the 14.17 -documentation and/or other materials provided with the distribution. 14.18 - 14.19 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14.20 -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14.21 -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 14.22 -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 14.23 -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 14.24 -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 14.25 -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 14.26 -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 14.27 -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 14.28 -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14.29 ---%> 14.30 -<%@page contentType="text/html" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %> 14.31 -<%@page import="de.uapcore.lightpit.Constants" %> 14.32 -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 14.33 -<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 14.34 -<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 14.35 - 14.36 -<%-- Define an alias for the main menu --%> 14.37 -<c:set scope="page" var="mainMenu" value="${requestScope[Constants.REQ_ATTR_MENU]}"/> 14.38 - 14.39 -<!DOCTYPE html> 14.40 -<html> 14.41 - <head> 14.42 - <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"> 14.43 - <title>LightPIT - TODO: current menu</title> 14.44 - <meta charset="UTF-8"> 14.45 - <link rel="stylesheet" href="lightpit.css" type="text/css"> 14.46 - </head> 14.47 - <body> 14.48 - 14.49 - <div id="mainMenu"> 14.50 - <c:forEach var="menuEntry" items="${mainMenu}"> 14.51 - <div class="menuEntry" 14.52 - <c:if test="${fn:contains(requestScope[Constants.REQ_ATTR_PATH], menuEntry.pathName)}"> 14.53 - data-active 14.54 - </c:if> 14.55 - > 14.56 - <a href="${menuEntry.pathName}"> 14.57 - <fmt:bundle basename="${menuEntry.resourceKey.bundle}"> 14.58 - <fmt:message key="${menuEntry.resourceKey.key}" /> 14.59 - </fmt:bundle> 14.60 - </a> 14.61 - </div> 14.62 - </c:forEach> 14.63 - 14.64 - </div> 14.65 - <div id="subMenu"> 14.66 - 14.67 - </div> 14.68 - <div id="content-area"> 14.69 - TODO: content fragment 14.70 - </div> 14.71 - </body> 14.72 -</html>
15.1 --- a/web/lightpit.css Sat Dec 16 20:19:28 2017 +0100 15.2 +++ b/web/lightpit.css Sun Dec 17 01:45:28 2017 +0100 15.3 @@ -43,7 +43,7 @@ 15.4 } 15.5 15.6 a { 15.7 - color: #3030f8; 15.8 + color: #3060f8; 15.9 text-decoration: none; 15.10 } 15.11 15.12 @@ -60,7 +60,7 @@ 15.13 #subMenu { 15.14 background: #f7f7ff; 15.15 15.16 - border-image: linear-gradient(to right, #606060, rgba(60,60,60,.1)); 15.17 + border-image: linear-gradient(to right, #606060, rgba(60,60,60,.25)); 15.18 border-image-slice: 1; 15.19 border-top-style: solid; 15.20 border-top-width: 1pt;