Про встраивание веб-сервера Jetty написано много руководств, где чаще всего приводят такой код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class WebApp { public static void main(String[] args) throws Exception { Server server = new Server(8080); WebAppContext context = new WebAppContext(); context.setDescriptor("/WEB-INF/web.xml"); context.setResourceBase("../src/main/webapp"); context.setContextPath("/"); context.setParentLoaderPriority(true); server.setHandler(context); server.start(); server.join(); } } |
И тут нас ждёт разочарование — мы должны запускать Jetty с ресурсами из файловой системы:
- «/WEB-INF/web.xml»
- «../src/main/webapp»
Появляется ощущение, что нас кто-то обманул, и на душе горько.
Меня не остановил этот провал, и я пошёл до конца.
Требуем загрузки ресурсов из ClassPath
Соображение первое — Jetty в своих недрах каким-то образом извлекает ресурсы. А значит стоит переопределить доступ к ним, чтобы учитывались ресурсы из Java ClassPath.
Сначала добьёмся старта сервера с дескриптором web.xml из ClassPath. Переопределяем WebXmlConfiguration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebXmlConfiguration; import java.io.IOException; public class ClasspathWebXmlConfiguration extends WebXmlConfiguration { @Override protected Resource findWebXml(WebAppContext context) throws IOException { Resource webXml = super.findWebXml(context); if ((webXml == null || !webXml.exists()) && context.getClassLoader() != null) { webXml = Resource.newResource( context.getClassLoader().getResource(context.getDescriptor())); } return webXml; } } |
Но помимо этого, в нашем приложении могут использоваться другие файлы конфигурации из WEB-INF, например файлы Spring Framework.
Соображение второе — не стоит пытаться достать из ClassPath что угодно, названия файлов нужно знать заранее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import java.io.IOException; import java.net.MalformedURLException; import java.util.Set; public class ClasspathWebAppContext extends WebAppContext { public Set<String> publishedClasspathResources; public ClasspathWebAppContext(Set<String> publishedClasspathResources) { this.publishedClasspathResources = publishedClasspathResources; } @Override public Resource getResource(String uriInContext) throws MalformedURLException { if (publishedClasspathResources.contains(uriInContext) && getClassLoader() != null) { try { if (uriInContext.startsWith("/")) uriInContext = uriInContext.substring(1); return Resource.newResource(getClassLoader().getResource(uriInContext)); } catch (IOException e) { throw new RuntimeException("Could not found published resource in classpath", e); } } return super.getResource(uriInContext); } } |
И вуаля — мы можем запустить сервер без использования ресурсов из файловой системы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; import java.io.File; import java.util.Arrays; import java.util.HashSet; import java.util.List; public class RemoteServer { private static final List<String> publishedResources = Arrays.asList( "/web.xml", "/spring.xml", "/web-spring.xml" ); public static void main(String[] args) { Server server = new Server(8080); WebAppContext context = new ClasspathWebAppContext( new HashSet<String>(publishedResources)); context.setConfigurations(new Configuration[]{new ClasspathWebXmlConfiguration()}); context.setDescriptor("web.xml"); context.setResourceBase("."); context.setContextPath("/"); context.setParentLoaderPriority(true); context.setClassLoader(Thread.currentThread().getContextClassLoader()); server.setHandler(context); try { server.start(); server.join(); } catch (Exception e) { if (!(e instanceof InterruptedException)) { System.out.print("Server stopped\n" + e.getMessage()); e.printStackTrace(System.out); } } } } |
Радуемся, пляшем, ведь вместо написания TCP сервера мы сможем использовать удобные Spring контроллеры.
Из пушки по воробьям, всё ради скорости разработки!