├── .gitignore ├── src ├── main │ └── java │ │ ├── META-INF │ │ └── webapp │ │ │ └── WEB-INF │ │ │ └── view │ │ │ ├── css │ │ │ └── style.css │ │ │ ├── images │ │ │ └── favicon.ico │ │ │ └── index.jsp │ │ └── com │ │ └── sjl │ │ ├── config │ │ ├── ApplicationModule.java │ │ ├── WebAppInitializer.java │ │ └── WebModule.java │ │ ├── web │ │ └── Home.java │ │ ├── Main.java │ │ ├── WebServerConfig.java │ │ └── WebServer.java └── test │ └── java │ └── com │ └── sjl │ └── IDE.java ├── README.markdown └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | var 3 | dependency-reduced-pom.xml 4 | .classpath 5 | .project 6 | .settings 7 | -------------------------------------------------------------------------------- /src/main/java/META-INF/webapp/WEB-INF/view/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding:0px; 3 | margin:0px; 4 | font-family:verdana; 5 | } 6 | 7 | h1 { 8 | color:blue; 9 | } -------------------------------------------------------------------------------- /src/main/java/META-INF/webapp/WEB-INF/view/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steveliles/jetty-embedded-spring-mvc-noxml/HEAD/src/main/java/META-INF/webapp/WEB-INF/view/images/favicon.ico -------------------------------------------------------------------------------- /src/main/java/META-INF/webapp/WEB-INF/view/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

It works!

7 |

Hi <%="me"%>!

8 | 9 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | A simple example project that sets up an embedded Jetty 8 with Spring-MVC, JSP and JSTL support. 2 | 3 | This example uses no XML configuration. 4 | 5 | Further description [here](http://steveliles.github.io/setting_up_embedded_jetty_8_and_spring_mvc_with_maven.html). 6 | -------------------------------------------------------------------------------- /src/test/java/com/sjl/IDE.java: -------------------------------------------------------------------------------- 1 | package com.sjl; 2 | 3 | public class IDE { 4 | // This class is a place-holder used to identify whether the 5 | // web-server is running in an IDE where the test class hierarchy 6 | // is available or in "production" mode (test classes removed) 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/config/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package com.sjl.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | 5 | @Configuration 6 | public class ApplicationModule 7 | { 8 | // Declare "application" scope beans here (ie., beans that are not only used by the web context) 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/web/Home.java: -------------------------------------------------------------------------------- 1 | package com.sjl.web; 2 | 3 | import org.springframework.stereotype.*; 4 | import org.springframework.web.bind.annotation.*; 5 | import org.springframework.web.servlet.*; 6 | 7 | @Controller 8 | public class Home { 9 | 10 | @RequestMapping("/") 11 | public ModelAndView home() 12 | { 13 | return new ModelAndView("index"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/Main.java: -------------------------------------------------------------------------------- 1 | package com.sjl; 2 | 3 | public class Main 4 | { 5 | public static void main(String... anArgs) throws Exception 6 | { 7 | new Main().start(); 8 | } 9 | 10 | private WebServer server; 11 | 12 | public Main() 13 | { 14 | server = new WebServer( 15 | WebServerConfig.Factory.newDevelopmentConfig("happy", 8000, "localhost")); 16 | } 17 | 18 | public void start() throws Exception 19 | { 20 | server.start(); 21 | server.join(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/config/WebAppInitializer.java: -------------------------------------------------------------------------------- 1 | package com.sjl.config; 2 | 3 | import javax.servlet.*; 4 | 5 | import org.apache.jasper.servlet.*; 6 | import org.springframework.web.*; 7 | import org.springframework.web.context.*; 8 | import org.springframework.web.context.support.*; 9 | import org.springframework.web.servlet.*; 10 | 11 | public class WebAppInitializer implements WebApplicationInitializer 12 | { 13 | private static final String JSP_SERVLET_NAME = "jsp"; 14 | private static final String DISPATCHER_SERVLET_NAME = "dispatcher"; 15 | 16 | @Override 17 | public void onStartup(ServletContext aServletContext) throws ServletException 18 | { 19 | registerListener(aServletContext); 20 | registerDispatcherServlet(aServletContext); 21 | registerJspServlet(aServletContext); 22 | } 23 | 24 | private void registerListener(ServletContext aContext) 25 | { 26 | AnnotationConfigWebApplicationContext _root = createContext(ApplicationModule.class); 27 | aContext.addListener(new ContextLoaderListener(_root)); 28 | } 29 | 30 | private void registerDispatcherServlet(ServletContext aContext) 31 | { 32 | AnnotationConfigWebApplicationContext _ctx = createContext(WebModule.class); 33 | ServletRegistration.Dynamic _dispatcher = 34 | aContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(_ctx)); 35 | _dispatcher.setLoadOnStartup(1); 36 | _dispatcher.addMapping("/"); 37 | } 38 | 39 | private void registerJspServlet(ServletContext aContext) { 40 | ServletRegistration.Dynamic _dispatcher = 41 | aContext.addServlet(JSP_SERVLET_NAME, new JspServlet()); 42 | _dispatcher.setLoadOnStartup(1); 43 | _dispatcher.addMapping("*.jsp"); 44 | } 45 | 46 | private AnnotationConfigWebApplicationContext createContext(final Class... aModules) 47 | { 48 | AnnotationConfigWebApplicationContext _ctx = new AnnotationConfigWebApplicationContext(); 49 | _ctx.register(aModules); 50 | return _ctx; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/config/WebModule.java: -------------------------------------------------------------------------------- 1 | package com.sjl.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.servlet.ViewResolver; 7 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 9 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 11 | import org.springframework.web.servlet.view.JstlView; 12 | import org.springframework.web.servlet.view.UrlBasedViewResolver; 13 | 14 | @EnableWebMvc 15 | @Configuration 16 | @ComponentScan(basePackages={"com.sjl"}) 17 | public class WebModule extends WebMvcConfigurerAdapter 18 | { 19 | @Override 20 | public void addViewControllers(ViewControllerRegistry aRegistry) 21 | { 22 | aRegistry.addViewController("/").setViewName("index"); 23 | } 24 | 25 | @Override 26 | public void addResourceHandlers(ResourceHandlerRegistry aRegistry) 27 | { 28 | aRegistry.addResourceHandler("/s/*").addResourceLocations("classpath:/META-INF/webapp/WEB-INF/view/scripts/*"); 29 | aRegistry.addResourceHandler("/c/*").addResourceLocations("classpath:/META-INF/webapp/WEB-INF/view/css/*"); 30 | aRegistry.addResourceHandler("/i/*").addResourceLocations("classpath:/META-INF/webapp/WEB-INF/view/images/*"); 31 | aRegistry.addResourceHandler("/WEB-INF/view/*").addResourceLocations("classpath:/META-INF/webapp/WEB-INF/view/*"); 32 | aRegistry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/META-INF/webapp/WEB-INF/view/images/favicon.ico"); 33 | } 34 | 35 | @Bean 36 | public ViewResolver viewResolver() 37 | { 38 | UrlBasedViewResolver viewResolver = new UrlBasedViewResolver(); 39 | viewResolver.setViewClass(JstlView.class); 40 | viewResolver.setPrefix("WEB-INF/view/"); 41 | viewResolver.setSuffix(".jsp"); 42 | return viewResolver; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/WebServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.sjl; 2 | 3 | public interface WebServerConfig 4 | { 5 | public String getServerName(); 6 | 7 | public int getPort(); 8 | 9 | public String getHostInterface(); 10 | 11 | public int getMinThreads(); 12 | 13 | public int getMaxThreads(); 14 | 15 | public String getAccessLogDirectory(); 16 | 17 | public class Factory { 18 | 19 | public static WebServerConfig newDevelopmentConfig(String aName, int aPort, String anInterface) { 20 | return new Development(aName, aPort, anInterface); 21 | } 22 | 23 | public static WebServerConfig newProductionConfig(String aName, int aPort, String anInterface, int aMinThreads, int aMaxThreads) { 24 | return new Production(aName, aPort, anInterface, aMinThreads, aMaxThreads); 25 | } 26 | 27 | static abstract class AbstractWebServerConfig implements WebServerConfig 28 | { 29 | private String name; 30 | private int port; 31 | private String intf; 32 | private int minThreads; 33 | private int maxThreads; 34 | 35 | private AbstractWebServerConfig(String aName, int aPort, String anInterface, int aMinThreads, int aMaxThreads) 36 | { 37 | name = aName; 38 | port = aPort; 39 | intf = anInterface; 40 | minThreads = aMinThreads; 41 | maxThreads = aMaxThreads; 42 | } 43 | 44 | @Override 45 | public String getServerName() 46 | { 47 | return name; 48 | } 49 | 50 | @Override 51 | public int getPort() 52 | { 53 | return port; 54 | } 55 | 56 | @Override 57 | public String getHostInterface() 58 | { 59 | return intf; 60 | } 61 | 62 | @Override 63 | public int getMinThreads() 64 | { 65 | return minThreads; 66 | } 67 | 68 | @Override 69 | public int getMaxThreads() 70 | { 71 | return maxThreads; 72 | } 73 | 74 | @Override 75 | public String getAccessLogDirectory() 76 | { 77 | return String.format("./var/logs/$s/", name); 78 | } 79 | } 80 | 81 | public static final class Development extends AbstractWebServerConfig 82 | { 83 | public Development(String aName, int aPort, String anInterface) 84 | { 85 | super(aName, aPort, anInterface, 5, 15); 86 | } 87 | } 88 | 89 | public static final class Production extends AbstractWebServerConfig 90 | { 91 | public Production(String aName, int aPort, String anInterface, int aMinThreads, int aMaxThreads) 92 | { 93 | super(aName, aPort, anInterface, aMinThreads, aMaxThreads); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.sjl 6 | jetty-noxml 7 | 1.0-SNAPSHOT 8 | jetty-noxml 9 | 10 | 11 | 12 | 13 | springsource-repo 14 | SpringSource Repository 15 | http://repo.springsource.org/release 16 | 17 | 18 | 19 | 20 | UTF-8 21 | 8.1.9.v20130131 22 | 8.1.4.v20120524 23 | 3.1.2.RELEASE 24 | 25 | 26 | 27 | 28 | 29 | src/main/java 30 | 31 | **/*.java 32 | 33 | 34 | 35 | 36 | 37 | ./ 38 | src/test/java 39 | 40 | **/*.java 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-source-plugin 48 | 2.1.2 49 | 50 | 51 | attach-sources 52 | verify 53 | 54 | jar-no-fork 55 | 56 | 57 | 58 | 59 | 60 | maven-compiler-plugin 61 | 2.3.2 62 | 63 | 1.6 64 | 1.6 65 | 66 | 67 | 68 | maven-eclipse-plugin 69 | 2.9 70 | 71 | true 72 | 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-shade-plugin 77 | 2.0 78 | 79 | 80 | 81 | shade 82 | 83 | 84 | 85 | 86 | *:* 87 | 88 | **/*.DSA 89 | **/*.RSA 90 | **/*.SF 91 | 92 | 93 | 94 | 95 | 96 | 97 | com.sjl.Main 98 | 99 | 100 | META-INF/spring.handlers 101 | 102 | 103 | META-INF/spring.schemas 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.springframework 118 | spring-context 119 | ${spring.version} 120 | 121 | 122 | org.springframework 123 | spring-webmvc 124 | ${spring.version} 125 | 126 | 127 | cglib 128 | cglib 129 | 2.2.2 130 | 131 | 132 | 133 | 134 | org.eclipse.jetty 135 | jetty-server 136 | ${jetty.version} 137 | 138 | 139 | org.eclipse.jetty 140 | jetty-servlet 141 | ${jetty.version} 142 | 143 | 144 | org.eclipse.jetty 145 | jetty-webapp 146 | ${jetty.version} 147 | 148 | 149 | org.eclipse.jetty 150 | jetty-servlets 151 | ${jetty.version} 152 | 153 | 154 | org.eclipse.jetty 155 | jetty-annotations 156 | ${jetty.version} 157 | 158 | 159 | 160 | 161 | org.eclipse.jetty 162 | jetty-jsp 163 | ${jetty.jsp.version} 164 | 165 | 166 | javax.servlet 167 | jstl 168 | 1.2 169 | provided 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/main/java/com/sjl/WebServer.java: -------------------------------------------------------------------------------- 1 | package com.sjl; 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | import java.util.*; 6 | 7 | import org.eclipse.jetty.annotations.*; 8 | import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler; 9 | import org.eclipse.jetty.server.*; 10 | import org.eclipse.jetty.server.handler.*; 11 | import org.eclipse.jetty.server.nio.*; 12 | import org.eclipse.jetty.util.resource.*; 13 | import org.eclipse.jetty.util.thread.*; 14 | import org.eclipse.jetty.webapp.*; 15 | 16 | public class WebServer 17 | { 18 | public static interface WebContext 19 | { 20 | public File getWarPath(); 21 | public String getContextPath(); 22 | } 23 | 24 | private Server server; 25 | private WebServerConfig config; 26 | 27 | public WebServer(WebServerConfig aConfig) 28 | { 29 | config = aConfig; 30 | } 31 | 32 | public void start() throws Exception 33 | { 34 | server = new Server(); 35 | 36 | server.setThreadPool(createThreadPool()); 37 | server.addConnector(createConnector()); 38 | server.setHandler(createHandlers()); 39 | server.setStopAtShutdown(true); 40 | 41 | server.start(); 42 | } 43 | 44 | public void join() throws InterruptedException 45 | { 46 | server.join(); 47 | } 48 | 49 | public void stop() throws Exception 50 | { 51 | server.stop(); 52 | } 53 | 54 | private ThreadPool createThreadPool() 55 | { 56 | QueuedThreadPool _threadPool = new QueuedThreadPool(); 57 | _threadPool.setName(config.getServerName()); 58 | _threadPool.setMinThreads(config.getMinThreads()); 59 | _threadPool.setMaxThreads(config.getMaxThreads()); 60 | return _threadPool; 61 | } 62 | 63 | private SelectChannelConnector createConnector() 64 | { 65 | SelectChannelConnector _connector = new SelectChannelConnector(); 66 | _connector.setPort(config.getPort()); 67 | _connector.setHost(config.getHostInterface()); 68 | return _connector; 69 | } 70 | 71 | private HandlerCollection createHandlers() 72 | { 73 | WebAppContext _ctx = new WebAppContext(); 74 | _ctx.setContextPath("/"); 75 | _ctx.setBaseResource(Resource.newClassPathResource("META-INF/webapp")); 76 | 77 | _ctx.setConfigurations (new Configuration [] 78 | { 79 | new AnnotationConfiguration() 80 | { 81 | @Override 82 | public void configure(WebAppContext context) throws Exception { 83 | boolean metadataComplete = context.getMetaData().isMetaDataComplete(); 84 | context.addDecorator(new AnnotationDecorator(context)); 85 | 86 | 87 | //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any 88 | AnnotationParser parser = null; 89 | if (!metadataComplete) 90 | { 91 | //If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations 92 | if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) 93 | { 94 | _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); 95 | _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); 96 | _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context)); 97 | } 98 | } 99 | 100 | //Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the 101 | //classes so we can call their onStartup() methods correctly 102 | createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context)); 103 | 104 | if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) 105 | { 106 | parser = new AnnotationParser() 107 | { 108 | @Override 109 | public void parse(Resource aDir, ClassNameResolver aResolver) throws Exception { 110 | if (aDir.isDirectory()) 111 | super.parse(aDir, aResolver); 112 | else 113 | super.parse(aDir.getURI(), aResolver); 114 | } 115 | }; 116 | 117 | parse(context, parser); 118 | 119 | for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers) 120 | context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList()); 121 | } 122 | 123 | } 124 | 125 | private void parse(final WebAppContext context, AnnotationParser parser) throws Exception 126 | { 127 | List _resources = getResources(getClass().getClassLoader()); 128 | for (Resource _resource : _resources) 129 | { 130 | if (_resource == null) 131 | return; 132 | 133 | parser.clearHandlers(); 134 | for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers) 135 | { 136 | if (h instanceof AbstractDiscoverableAnnotationHandler) 137 | ((AbstractDiscoverableAnnotationHandler)h).setResource(null); // 138 | } 139 | parser.registerHandlers(_discoverableAnnotationHandlers); 140 | parser.registerHandler(_classInheritanceHandler); 141 | parser.registerHandlers(_containerInitializerAnnotationHandlers); 142 | 143 | parser.parse(_resource, new ClassNameResolver() 144 | { 145 | public boolean isExcluded (String name) 146 | { 147 | if (context.isSystemClass(name)) return true; 148 | if (context.isServerClass(name)) return false; 149 | return false; 150 | } 151 | 152 | public boolean shouldOverride (String name) 153 | { 154 | //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp? 155 | if (context.isParentLoaderPriority()) 156 | return false; 157 | return true; 158 | } 159 | }); 160 | } 161 | } 162 | 163 | private List getResources(ClassLoader aLoader) throws IOException 164 | { 165 | if (aLoader instanceof URLClassLoader) 166 | { 167 | List _result = new ArrayList(); 168 | URL[] _urls = ((URLClassLoader)aLoader).getURLs(); 169 | for (URL _url : _urls) 170 | _result.add(Resource.newResource(_url)); 171 | 172 | return _result; 173 | } 174 | return Collections.emptyList(); 175 | } 176 | } 177 | }); 178 | 179 | List _handlers = new ArrayList(); 180 | 181 | _handlers.add(_ctx); 182 | 183 | HandlerList _contexts = new HandlerList(); 184 | _contexts.setHandlers(_handlers.toArray(new Handler[0])); 185 | 186 | RequestLogHandler _log = new RequestLogHandler(); 187 | _log.setRequestLog(createRequestLog()); 188 | 189 | HandlerCollection _result = new HandlerCollection(); 190 | _result.setHandlers(new Handler[] {_contexts, _log}); 191 | 192 | return _result; 193 | } 194 | 195 | private RequestLog createRequestLog() 196 | { 197 | NCSARequestLog _log = new NCSARequestLog(); 198 | 199 | File _logPath = new File(config.getAccessLogDirectory() + "yyyy_mm_dd.request.log"); 200 | _logPath.getParentFile().mkdirs(); 201 | 202 | _log.setFilename(_logPath.getPath()); 203 | _log.setRetainDays(30); 204 | _log.setExtended(false); 205 | _log.setAppend(true); 206 | _log.setLogTimeZone("UTC"); 207 | _log.setLogLatency(true); 208 | return _log; 209 | } 210 | } --------------------------------------------------------------------------------