├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
└── java
└── com
├── twilio
└── wiztowar
│ ├── WarAssetsBundle.java
│ └── DWAdapter.java
└── yammer
└── dropwizard
└── config
└── ExtendedEnvironment.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .idea
3 | *.iml
4 | *.class
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Twilio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WizToWar - Have your cake and eat it too
2 | ========================================
3 |
4 | WizToWar is a simple library that enables a [Dropwizard](http://dropwizard.io) service to also be deployable in a WAR container such as Tomcat.
5 |
6 | By following the steps in the usage section below you will be able to create
7 | both a Dropwizard jar and a WAR of the same service.
8 |
9 |
10 | Caveat emptor:
11 | --------------
12 |
13 | * Only tested on Tomcat 7
14 | * No support for bundles
15 | * Many features untested
16 | * Goes against the whole philosophy of Dropwizard...
17 |
18 | Usage
19 | ------
20 |
21 | Include the wiztowar jar as a dependency:
22 |
23 | ```xml
24 |
25 | com.twilio
26 | wiztowar
27 | 1.3
28 |
29 | ```
30 |
31 | Create a new class for your application like this:
32 |
33 | ```java
34 | package com.twilio.mixerstate;
35 |
36 | import com.google.common.io.Resources;
37 | import com.twilio.wiztowar.DWAdapter;
38 | import com.yammer.dropwizard.Service;
39 |
40 | import java.io.File;
41 | import java.net.URISyntaxException;
42 | import java.net.URL;
43 |
44 |
45 | public class MixerStateDWApplication extends DWAdapter {
46 | final static MixerStateService service = new MixerStateService();
47 |
48 | /**
49 | * Return the Dropwizard service you want to run.
50 | */
51 | public Service getSingletonService(){
52 | return service;
53 | }
54 |
55 | /**
56 | * Return the File where the configuration lives.
57 | */
58 | @Override
59 | public File getConfigurationFile() {
60 |
61 | URL url = Resources.getResource("mixer-state-server.yml");
62 | try {
63 | return new File(url.toURI());
64 | } catch (URISyntaxException e) {
65 | throw new IllegalStateException(e);
66 | }
67 | }
68 | }
69 | ```
70 |
71 |
72 | Create a main/webapp/WEB-INF/web.xml file:
73 | ------------------------------------------
74 |
75 | ```xml
76 |
77 |
78 |
79 |
80 | com.twilio.mixerstate.MixerStateDWApplication
81 |
82 |
83 | ```
84 |
85 | Make sure you also build a WAR artifact
86 | ---------------------------------------------
87 |
88 | There are two alternatives to building a war:
89 |
90 | ### a. Add instructions to also build a WAR
91 |
92 | This goes in `` section:
93 |
94 | ```xml
95 |
96 | org.apache.maven.plugins
97 | maven-war-plugin
98 | 2.4
99 |
100 |
101 | default-war
102 | package
103 |
104 | war
105 |
106 |
107 |
108 |
109 | target/webapp
110 |
111 |
112 | ```
113 |
114 | ### b. Change packaging of your Dropwizard service
115 |
116 | If you do not intend to run the Dropwizard service standalone, you can simply
117 | change the "packaging" element in pom.xml to be "war" instead of "jar".
118 |
119 |
120 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.twilio
6 | wiztowar
7 | 1.7-SNAPSHOT
8 | jar
9 |
10 | wiztowar
11 | WizToWar is a simple library that helps you deploy Dropwizard services as a WAR in a WAR container
12 |
13 | http://github.com/twilio/wiztowar
14 |
15 |
16 |
17 |
18 | christer
19 | Christer Fahlgren
20 | christer@twilio.com
21 |
22 |
23 |
24 |
25 | The MIT License
26 | http://www.opensource.org/licenses/mit-license.php
27 | repo
28 |
29 |
30 |
31 |
32 | 0.6.2
33 | 2.2.0
34 |
35 |
36 |
37 | scm:git:git://github.com/twilio/wiztowar.git
38 | scm:git:git@github.com:twilio/wiztowar.git
39 | http://github.com/twilio/wiztowar
40 |
41 |
42 |
43 |
44 | sonatype-nexus-snapshots
45 | Sonatype Nexus snapshot repository
46 | https://oss.sonatype.org/content/repositories/snapshots
47 |
48 |
49 | sonatype-nexus-staging
50 | Sonatype Nexus release repository
51 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
52 |
53 |
54 |
55 |
56 |
57 | com.yammer.dropwizard
58 | dropwizard-core
59 | ${dropwizard.version}
60 |
61 |
62 | com.yammer.metrics
63 | metrics-annotation
64 | ${metrics.version}
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-release-plugin
73 | 2.2.2
74 |
75 | -Dgpg.passphrase=${gpg.passphrase}
76 |
77 |
78 |
79 |
80 |
81 |
82 | release-sign-artifacts
83 |
84 |
85 | performRelease
86 | true
87 |
88 |
89 |
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-gpg-plugin
94 | 1.4
95 |
96 | ${gpg.passphrase}
97 |
98 |
99 |
100 | sign-artifacts
101 | verify
102 |
103 | sign
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/main/java/com/twilio/wiztowar/WarAssetsBundle.java:
--------------------------------------------------------------------------------
1 | package com.twilio.wiztowar;
2 |
3 | import com.yammer.dropwizard.Bundle;
4 | import com.yammer.dropwizard.assets.AssetServlet;
5 | import com.yammer.dropwizard.config.Bootstrap;
6 | import com.yammer.dropwizard.config.Environment;
7 |
8 | import static com.google.common.base.Preconditions.checkArgument;
9 |
10 | /**
11 | * A variant of the {@link com.yammer.dropwizard.assets.AssetsBundle} included in Dropwizard
12 | * suited for deployments where there is a non-empty servlet context.
13 | */
14 | public class WarAssetsBundle implements Bundle {
15 | private static final String DEFAULT_INDEX_FILE = "index.htm";
16 | private static final String DEFAULT_PATH = "/assets";
17 |
18 | private final String resourcePath;
19 | private final String warPath;
20 | private final String uriPath;
21 | private final String indexFile;
22 |
23 | /**
24 | * Creates a new {@link WarAssetsBundle} which serves up static assets from
25 | * {@code src/main/resources/assets/*} as {@code /assets/*}.
26 | *
27 | * @see com.twilio.wiztowar.WarAssetsBundle#WarAssetsBundle(String, String, String, String)
28 | */
29 | public WarAssetsBundle() {
30 | this("", DEFAULT_PATH, DEFAULT_INDEX_FILE,"");
31 | }
32 |
33 | /**
34 | * Creates a new {@link WarAssetsBundle} which will configure the service to serve the static files
35 | * located in {@code src/main/resources/${path}} as {@code /${path}}. For example, given a
36 | * {@code path} of {@code "/assets"}, {@code src/main/resources/assets/example.js} would be
37 | * served up from {@code /assets/example.js}.
38 | *
39 | * @param path the classpath and URI root of the static asset files
40 | * @see com.twilio.wiztowar.WarAssetsBundle#WarAssetsBundle(String, String, String, String)
41 | */
42 | public WarAssetsBundle(String path) {
43 | this(path, path, DEFAULT_INDEX_FILE,"");
44 | }
45 |
46 | /**
47 | * Creates a new AssetsBundle which will configure the service to serve the static files
48 | * located in {@code src/main/resources/${resourcePath}} as {@code /${uriPath}}. For example, given a
49 | * {@code resourcePath} of {@code "/assets"} and a uriPath of {@code "/js"},
50 | * {@code src/main/resources/assets/example.js} would be served up from {@code /js/example.js}.
51 | *
52 | * @param resourcePath the resource path (in the classpath) of the static asset files
53 | * @param uriPath the uri path for the static asset files
54 | * @see com.twilio.wiztowar.WarAssetsBundle#WarAssetsBundle(String, String, String, String)
55 | */
56 | public WarAssetsBundle(String resourcePath, String uriPath) {
57 | this(resourcePath, uriPath, DEFAULT_INDEX_FILE,"");
58 | }
59 |
60 | /**
61 | * Creates a new AssetsBundle which will configure the service to serve the static files
62 | * located in {@code src/main/resources/${resourcePath}} as {@code /${uriPath}}. If no file name is
63 | * in ${uriPath}, ${indexFile} is appended before serving. For example, given a
64 | * {@code resourcePath} of {@code "/assets"} and a uriPath of {@code "/js"},
65 | * {@code src/main/resources/assets/example.js} would be served up from {@code /js/example.js}.
66 | *
67 | * @param warPath the path to the application - typically the war-name in an app-server
68 | * @param resourcePath the resource path (in the classpath) of the static asset files
69 | * @param uriPath the uri path for the static asset files
70 | * @param indexFile the name of the index file to use
71 | */
72 | public WarAssetsBundle(String warPath, String resourcePath, String uriPath, String indexFile) {
73 | checkArgument(resourcePath.startsWith("/"), "%s is not an absolute path", resourcePath);
74 | checkArgument(!"/".equals(resourcePath), "%s is the classpath root", resourcePath);
75 | this.resourcePath = resourcePath.endsWith("/") ? resourcePath : (resourcePath + '/');
76 | this.uriPath = uriPath.endsWith("/") ? uriPath : (uriPath + '/');
77 | this.indexFile = indexFile;
78 | this.warPath = warPath;
79 | }
80 |
81 | @Override
82 | public void initialize(Bootstrap> bootstrap) {
83 | // nothing doing
84 | }
85 |
86 | @Override
87 | public void run(Environment environment) {
88 | environment.addServlet(createServlet(), uriPath + '*');
89 | }
90 |
91 | private AssetServlet createServlet() {
92 | final String warPathWithTrailingSlash = warPath.endsWith("/") ? warPath : warPath + "/";
93 | final String warPathWithStartingSlash = warPathWithTrailingSlash
94 | .startsWith("/") ? warPathWithTrailingSlash : "/" + warPathWithTrailingSlash;
95 | final String uriPathSansStartingSlash = uriPath.startsWith("/") ? uriPath.substring(1) : uriPath;
96 | return new AssetServlet(resourcePath, warPathWithStartingSlash + uriPathSansStartingSlash, indexFile);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/yammer/dropwizard/config/ExtendedEnvironment.java:
--------------------------------------------------------------------------------
1 | package com.yammer.dropwizard.config;
2 |
3 | import com.google.common.collect.*;
4 | import com.sun.jersey.core.reflection.AnnotatedMethod;
5 | import com.sun.jersey.core.reflection.MethodList;
6 | import com.yammer.dropwizard.json.ObjectMapperFactory;
7 | import com.yammer.dropwizard.tasks.Task;
8 | import com.yammer.dropwizard.validation.Validator;
9 | import com.yammer.metrics.core.HealthCheck;
10 | import org.eclipse.jetty.servlet.FilterHolder;
11 | import org.eclipse.jetty.servlet.ServletHolder;
12 | import org.eclipse.jetty.util.resource.Resource;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import javax.ws.rs.HttpMethod;
17 | import javax.ws.rs.Path;
18 | import java.util.EventListener;
19 |
20 | /**
21 | * This class is here to enable us to pull out various things from the {@link Environment}.
22 | */
23 | public class ExtendedEnvironment extends Environment {
24 |
25 | final private static Logger logger = LoggerFactory.getLogger(ExtendedEnvironment.class);
26 | /**
27 | * Creates a new environment.
28 | *
29 | * @param name the name of the service
30 | * @param configuration the service's {@link com.yammer.dropwizard.config.Configuration}
31 | * @param objectMapperFactory the {@link com.yammer.dropwizard.json.ObjectMapperFactory} for the service
32 | */
33 | public ExtendedEnvironment(String name,
34 | Configuration configuration,
35 | ObjectMapperFactory objectMapperFactory,
36 | Validator validator) {
37 | super(name, configuration, objectMapperFactory, validator);
38 |
39 | }
40 |
41 | @Override
42 | public ImmutableSet getTasks() {
43 | return super.getTasks();
44 | }
45 |
46 | @Override
47 | public ImmutableSet getHealthChecks() {
48 | return super.getHealthChecks();
49 | }
50 |
51 | @Override
52 | public Resource getBaseResource() {
53 | return super.getBaseResource();
54 | }
55 |
56 | @Override
57 | public ImmutableSet getProtectedTargets() {
58 | return super.getProtectedTargets();
59 | }
60 |
61 | @Override
62 | public ImmutableMap getServlets() {
63 | return super.getServlets();
64 | }
65 |
66 | @Override
67 | public ImmutableMultimap getFilters() {
68 | return super.getFilters();
69 | }
70 |
71 | @Override
72 | public ImmutableSet getServletListeners() {
73 | return super.getServletListeners();
74 | }
75 |
76 | /**
77 | * Log all the resources.
78 | */
79 | private void logResources() {
80 | final ImmutableSet.Builder builder = ImmutableSet.builder();
81 |
82 | for (Class> klass : super.getJerseyResourceConfig().getClasses()) {
83 | if (klass.isAnnotationPresent(Path.class)) {
84 | builder.add(klass.getCanonicalName());
85 | }
86 | }
87 |
88 | for (Object o : super.getJerseyResourceConfig().getSingletons()) {
89 | if (o.getClass().isAnnotationPresent(Path.class)) {
90 | builder.add(o.getClass().getCanonicalName());
91 | }
92 | }
93 |
94 | logger.debug("resources = {}", builder.build());
95 | }
96 |
97 | /**
98 | * Log all endpoints.
99 | */
100 | public void logEndpoints(Configuration configuration) {
101 | final StringBuilder stringBuilder = new StringBuilder(1024).append("The following paths were found for the configured resources:\n\n");
102 |
103 | final ImmutableList.Builder> builder = ImmutableList.builder();
104 | for (Object o : super.getJerseyResourceConfig().getSingletons()) {
105 | if (o.getClass().isAnnotationPresent(Path.class)) {
106 | builder.add(o.getClass());
107 | }
108 | }
109 | for (Class> klass : super.getJerseyResourceConfig().getClasses()) {
110 | if (klass.isAnnotationPresent(Path.class)) {
111 | builder.add(klass);
112 | }
113 | }
114 |
115 | for (Class> klass : builder.build()) {
116 | final String path = klass.getAnnotation(Path.class).value();
117 | String rootPath = configuration.getHttpConfiguration().getRootPath();
118 | if (rootPath.endsWith("/*")) {
119 | rootPath = rootPath.substring(0, rootPath.length() - (path.startsWith("/") ? 2 : 1));
120 | }
121 |
122 | final ImmutableList.Builder endpoints = ImmutableList.builder();
123 | for (AnnotatedMethod method : annotatedMethods(klass)) {
124 | final StringBuilder pathBuilder = new StringBuilder()
125 | .append(rootPath)
126 | .append(path);
127 | if (method.isAnnotationPresent(Path.class)) {
128 | final String methodPath = method.getAnnotation(Path.class).value();
129 | if (!methodPath.startsWith("/") && !path.endsWith("/")) {
130 | pathBuilder.append('/');
131 | }
132 | pathBuilder.append(methodPath);
133 | }
134 | for (HttpMethod verb : method.getMetaMethodAnnotations(HttpMethod.class)) {
135 | endpoints.add(String.format(" %-7s %s (%s)",
136 | verb.value(),
137 | pathBuilder.toString(),
138 | klass.getCanonicalName()));
139 | }
140 | }
141 |
142 | for (String line : Ordering.natural().sortedCopy(endpoints.build())) {
143 | stringBuilder.append(line).append('\n');
144 | }
145 | }
146 |
147 | logger.info(stringBuilder.toString());
148 | }
149 |
150 |
151 | private MethodList annotatedMethods(Class> resource) {
152 | return new MethodList(resource, true).hasMetaAnnotation(HttpMethod.class);
153 | }
154 |
155 | /**
156 | * Validate the Jersey resources before launching.
157 | */
158 | public void validateJerseyResources() {
159 | logResources();
160 | }
161 | }
--------------------------------------------------------------------------------
/src/main/java/com/twilio/wiztowar/DWAdapter.java:
--------------------------------------------------------------------------------
1 | package com.twilio.wiztowar;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.IOException;
6 | import java.util.EventListener;
7 | import java.util.HashSet;
8 | import java.util.Map;
9 | import java.util.Set;
10 |
11 | import javax.servlet.FilterRegistration;
12 | import javax.servlet.ServletContext;
13 | import javax.servlet.ServletContextEvent;
14 | import javax.servlet.ServletContextListener;
15 | import javax.servlet.ServletRegistration;
16 | import javax.servlet.ServletRegistration.Dynamic;
17 | import javax.servlet.http.HttpServlet;
18 | import javax.ws.rs.core.Application;
19 |
20 | import org.eclipse.jetty.servlet.FilterHolder;
21 | import org.eclipse.jetty.servlet.ServletHolder;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import com.google.common.collect.ImmutableMap;
26 | import com.yammer.dropwizard.Service;
27 | import com.yammer.dropwizard.config.Bootstrap;
28 | import com.yammer.dropwizard.config.Configuration;
29 | import com.yammer.dropwizard.config.ConfigurationException;
30 | import com.yammer.dropwizard.config.ConfigurationFactory;
31 | import com.yammer.dropwizard.config.ExtendedEnvironment;
32 | import com.yammer.dropwizard.config.HttpConfiguration;
33 | import com.yammer.dropwizard.config.LoggingFactory;
34 | import com.yammer.dropwizard.jersey.JacksonMessageBodyProvider;
35 | import com.yammer.dropwizard.json.ObjectMapperFactory;
36 | import com.yammer.dropwizard.servlets.ThreadNameFilter;
37 | import com.yammer.dropwizard.tasks.TaskServlet;
38 | import com.yammer.dropwizard.util.Generics;
39 | import com.yammer.dropwizard.validation.Validator;
40 | import com.yammer.metrics.HealthChecks;
41 | import com.yammer.metrics.core.HealthCheck;
42 | import com.yammer.metrics.reporting.AdminServlet;
43 | import com.yammer.metrics.util.DeadlockHealthCheck;
44 |
45 | /**
46 | * The {@link DWAdapter} adapts a Dropwizard {@link Service} to be hooked in to the lifecycle of a WAR.
47 | */
48 | public abstract class DWAdapter extends Application implements ServletContextListener {
49 |
50 | /**
51 | * The {@link ServletContext} that Dropwizard will run in.
52 | */
53 | private static ServletContext servletContext;
54 |
55 | /**
56 | * The {@link Logger} to use.
57 | */
58 | final static Logger logger = LoggerFactory.getLogger(DWAdapter.class);
59 |
60 | /**
61 | * The Jersey singletons.
62 | */
63 | private HashSet