Giter Site home page Giter Site logo

dropwizard / dropwizard Goto Github PK

View Code? Open in Web Editor NEW
8.5K 375.0 3.4K 108.33 MB

A damn simple library for building production-ready RESTful web services.

Home Page: https://www.dropwizard.io

License: Apache License 2.0

Shell 0.06% Java 99.88% FreeMarker 0.04% HTML 0.01% Mustache 0.02%
dropwizard java web-framework rest hibernate jersey2 jetty jax-rs jdbi jersey-framework

dropwizard's Introduction

Dropwizard

Build Quality Gate Status Maven Central Javadocs Documentation Status Maintainability Reproducible Builds Contribute with Gitpod

Dropwizard is a sneaky way of making fast Java web applications.

It's a little bit of opinionated glue code which bangs together a set of libraries which have historically not sucked:

Read more at dropwizard.io.

Want to contribute to Dropwizard?

Before working on the code, if you plan to contribute changes, please read the following CONTRIBUTING document.

Need help or found an issue?

When reporting an issue through the issue tracker on GitHub or sending an email to the Dropwizard User Google Group mailing list, please use the following guidelines:

  • Check existing issues to see if it has been addressed already
  • The version of Dropwizard you are using
  • A short description of the issue you are experiencing and the expected outcome
  • Description of how someone else can reproduce the problem
  • Paste error output or logs in your issue or in a Gist. If pasting them in the GitHub issue, wrap it in three backticks: ``` so that it renders nicely
  • Write a unit test to show the issue!

Sponsors

Dropwizard is generously supported by some companies with licenses and free accounts for their products.

JetBrains

JetBrains

JetBrains supports our open source project by sponsoring some All Products Packs within their Free Open Source License program.

dropwizard's People

Contributors

arteam avatar carlo-rtr avatar celkins avatar cmicali avatar codahale avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar evnm avatar freddeschenes avatar ghenkes avatar grahamoregan avatar joschi avatar jplock avatar kingbuzzer avatar michaelfairley avatar mveitas avatar nickbabcock avatar nicktelford avatar pkwarren avatar renovate-bot avatar renovate[bot] avatar rhowe avatar ryankennedy avatar skamille avatar spacanowski avatar tjcutajar avatar tomakehurst avatar wakandan avatar zuniquex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dropwizard's Issues

End to end integration testing

How about adding a class similar to this to dropwizard-testing for end to end integration testing?

public class ServiceIntegrationTest<T extends Service> {

  private static Log LOG = Log.forClass(ServiceIntegrationTest.class);

  protected Server server;
  protected int port, adminPort;

  /**
   * @see com.yammer.dropwizard.cli.ServerCommand#run
   */
  @BeforeMethod
  public void setUp() throws Exception {
    final Service service = createServiceInstance();

    int[] openPorts = getPossiblyOpenPorts(2);
    port = openPorts[0];
    adminPort = openPorts[1];

    File temporaryConfigFile = createTemporaryConfigFile(port, adminPort);

    ConfigurationFactory<?> factory = ConfigurationFactory.forClass(
        service.getConfigurationClass(), new Validator(), service.getJacksonModules());
    Configuration configuration = (Configuration) factory.build(temporaryConfigFile);

    Environment environment = new Environment(service, configuration);
    service.initializeWithBundles(configuration, environment);

    server = new ServerFactory(configuration.getHttpConfiguration(),
        service.getName()).buildServer(environment);

    LOG.info("Starting embedded Jetty with configuration {}", configuration.toString());
    server.start();
  }

  @AfterMethod
  public void tearDown() throws Exception {
    LOG.info("Stopping embedded Jetty server");
    server.stop();
    server.join();
  }

  /**
   * This method tries to find a set of open ports by creating a bunch of
   * server sockets.
   * <p/>
   * Note: This procedure is susceptible to a race condition and some
   * of the returned ports may be in use
   */
  private int[] getPossiblyOpenPorts(int number) throws IOException {
    ServerSocket[] serverSockets = new ServerSocket[number];
    try {
      int[] ports = new int[number];
      for (int i = 0; i < number; i++) {
        serverSockets[i] = new ServerSocket(0 /* bind to any open port */);
        ports[i] = serverSockets[i].getLocalPort();
      }
      return ports;

    } finally {
      for (ServerSocket socket : serverSockets) {
        socket.close();
      }
    }
  }

  public Service createServiceInstance() throws Exception {
    return getServiceClass().newInstance();
  }

  public String createConfigurationAsYaml() throws IOException {
    return Resources.toString(Resources.getResource("config/test-jetty-config.yml"), Charsets.UTF_8);
  }

  private File createTemporaryConfigFile(int port, int adminPort) throws IOException {
    File temporaryConfig = File.createTempFile("dropwizard", ".yml");
    temporaryConfig.deleteOnExit();

    Files.write(createConfigurationAsYaml() + "\n" +
        "http:\n" +
        "  port: " + port + "\n" +
        "  adminPort: " + adminPort + "\n",
        temporaryConfig, Charsets.UTF_8);

    return temporaryConfig;
  }

  /**
   * @see com.yammer.dropwizard.AbstractService#getConfigurationClass
   */
  @SuppressWarnings("unchecked")
  public final Class<T> getServiceClass() {
    Type t = getClass();
    while (t instanceof Class<?>) {
      t = ((Class<?>) t).getGenericSuperclass();
    }
    if (t instanceof ParameterizedType) {
      for (Type param : ((ParameterizedType) t).getActualTypeArguments()) {
        if (param instanceof Class<?>) {
          final Class<?> cls = (Class<?>) param;
          if (Service.class.isAssignableFrom(cls)) {
            return (Class<T>) cls;
          }
        }
      }
    }
    throw new IllegalStateException("Can not figure out Configuration type " +
        "parameterization for " + getClass().getName());
  }
}

Tasks servlet not finding tasks (404)

It seems that the upgrade of Jetty has changed the semantics of HttpServletRequest.getServletPath(), such that it's returning "/tasks" for all requests at the Tasks servlet. The method getPathInfo() seems to fit the bill:

"Returns any extra path information associated with the URL the client sent when it made this request. The extra path information follows the servlet path but precedes the query string and will start with a "/" character."

Pull request forthcoming.

Support DNS overrides via HttpClient 4.2's DnsResolver

We have a service our app connects to which is exposed via HTTPS. The cert contains the same host name for every environment (internally we use DNS switching to move between environments). In order to support connecting to this externally (where we don't control DNS) it would be great to have some config exposed that allows setting of name overrides by providing a DnsResolver to the apache http client.

I'm happy to write the code for this if it's likely to get merged.

Use AssetBundle on /

I'd like requests to / to be served by an AssetBundle (while still being able to set other sub-paths via @Path). When I try to do this with:

addBundle(new AssetsBundle("/assets/", 0, "/"));

I get the following error:

Exception in thread "main" java.lang.IllegalArgumentException: duplicate key: /*
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:115)
at com.google.common.collect.RegularImmutableMap.<init>(RegularImmutableMap.java:72)
at com.google.common.collect.ImmutableMap$Builder.fromEntryList(ImmutableMap.java:245)
at com.google.common.collect.ImmutableMap$Builder.build(ImmutableMap.java:231)
at com.yammer.dropwizard.config.Environment.getServlets(Environment.java:353)
at com.yammer.dropwizard.config.ServerFactory.createHandler(ServerFactory.java:180)
at com.yammer.dropwizard.config.ServerFactory.buildServer(ServerFactory.java:81)
at com.yammer.dropwizard.cli.ServerCommand.run(ServerCommand.java:50)
at com.yammer.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:71)
at com.yammer.dropwizard.cli.Command.run(Command.java:113)
at com.yammer.dropwizard.AbstractService.run(AbstractService.java:178)
at com.jamesward.BarService.main(BarService.java:11)

Performance: AssetServlet unnecessarily clones byte[] resource on every request

Although it's good engineering to make a defensive copy of the byte array in a public getter, it's unnecessary in this case because AssetServlet.CachedAsset is a private class and the byte array is effectively immutable (no other access modifies it).

The performance gain could be significant when serving large images.

The servlet's doGet method can safely access the private array directly:

output.write(cachedAsset.resource);

Handling of generic detection for ConfiguredCommand incomplete

Looks like ConfiguredCommand assumes that all commands directly extend it; so trying to use an intermediate base class, like:

class MyCommmand extends ConfiguredCommand<MyConfig> { }

will fail with exception like:

Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
at com.yammer.dropwizard.cli.ConfiguredCommand.getConfigurationClass(ConfiguredCommand.java:36)
at com.yammer.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:62)
at com.yammer.dropwizard.cli.Command.run(Command.java:113)
at com.yammer.dropwizard.AbstractService.run(AbstractService.java:178)

I suspect same is true for other generic types, but this is the one I bumped into.
A work-around is to keep intermediate classes parametric as well.

To fix this it should be possible to just go down inheritance hierarchy to find the place where generic information is passed; and although type aliasing can theoretically be problem (someone using intermediate classes with different number of parameters etc), in practice this should suffice.
(and if not, java-classmate project can solve this cleanly and completely -- but it'd be one more dependency, although can be shaded)

File not logging at INFO

I might be doing something stupid here, if so, apologies, but I have this logging configured

logging:
  level: DEBUG
  loggers:
    "net.amadan.sal.migration.MigrationResource": DEBUG
  console:
    enabled: true
    threshold: INFO
  file:
    enabled: true
    threshold: INFO
    currentLogFilename: migration.log
    archivedLogFilenamePattern: migration-%d.log.gz
    archivedFileCount: 50
    timeZone: UTC

and with that, nothing gets written to the log file. If I change file/threshold to DEBUG/ALL, I see file logging. Is that intentional?

Support finer grained control of security over the admin servlet

We'd like to be able to
a) Enforce SSL for admin servlet requests in addition to basic auth
b) Be more selective about which admin features are protected at all

Being able to add a servlet filter to the admin handler would be adequate for this.

Happy to submit a patch for this if it's likely to be included.

Make request logging non-blocking

No need to keep this around in the critical path. We should generate the log statement and then push it to a queue for a background thread to write to disk.

Add stack trace logging for slow requests

Steal heavily from dogslow and log the stack traces of threads which have been running the same request for > Nms.

We could use a hashed wheel timer for rough timeouts and then thread local ID counters -- when a request is handled, increment the thread's counter and schedule a job to execute in Nms which checks that counter against the result of the increment. If the thread is still running but the counter hasn't been incremented, get its stack trace and log it.

Add User Mailing List

Coda,

First of all I can't thank you (and Yammer) enough for releasing this. Best-of-breed REST stack, out-of-the-box metrics, etc. (I could go on and on...)

In an hour or so, I was able to migrate a bunch of our JAX-RS services to dropwizard and get all the metrics and health checks we really need... Truly awesome.

Anyway, is there a mailing list for users to interact? If not could you establish one (google groups, etc.)?

Thanks again, this is great.

  • Taylor

Appetite for Moustache Template Support

Curious as to the appetite for supporting moustache templates.

A couple weeks back I had done some experimental work in dropwizard-views to play around with the idea, it wasn't too difficult to add/cleanup a couple interfaces and provide support for both freemarker or moustache. They both work off a JavaBean model so it was basically just making the ViewMessageBodyWriter a little more extensible.

I realize these two frameworks are pretty much at either end of the templating landscape goes (logic vs. logicless), but if there's interest, I can tidy things up.

Cheers.

Minor improvement to Size, Duration, make jackson serializable

I noticed that two types, Size and Duration, are not by default serializable with Jackson. But it looks like 'toString()' method would work well, so the fix would be as simple as adding @JsonValue for 'toString()' methods. I assume this is ok since there is already @JsonCreator used; otherwise we could almost as easily create custom serializers, which is what I do locally.

My specific use case is debugging: I just want to print out Configuration object as JSON during startup.

Related to this, I guess it might be also useful to expose config object itself via GUI (similar to /threads etc); but I leave that for future improvements. :-)
(further ahead, really trying to dig into Jetty setup, JMX exposes only subset... but I digress).

Let me know if you want a patch (I realize that a unit test would be good, esp. for round-tripping); fix itself is trivial.

Make logback usage more configurable

Just some non configurable roadblocks I've hit:

  1. Make the pattern in com.yammer.dropwizard.logging.LogFormatter configurable (yes, the default is a well-thought-out choice but the relative merit of logging patterns is subjective and everyone will want to tweak theirs).
  2. Expose some of the other configurable attributes in the ch.qos.logback.classic.PatternLayout hierarchy (e.g., setting outputPatternAsPresentationHeader to false will suppress the printing of the logback pattern on startup).

Use Logback instead of log4j

This is more a suggestion than a bug, but I've been using Logback for my logging, and was curious if this was on the horizon or worth switching to? It is positioned to be the successor to log4j, and as such, they even provide a pretty lengthy explanation on why you should switch.

Anyways, love the project, and I'm itching to use it at work.

Certain tests are locale dependent

jbrauer@werner:~/projects/dropwizard (master)$ cat /home/jbrauer/projects/dropwizard/dropwizard-core/target/surefire-reports/com.yammer.dropwizard.config.tests.ConfigurationFactoryTest.txt
-------------------------------------------------------------------------------
Test set: com.yammer.dropwizard.config.tests.ConfigurationFactoryTest
-------------------------------------------------------------------------------
Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: -2,104,463.334 sec <<< FAILURE!
throwsAnExceptionOnInvalidFiles(com.yammer.dropwizard.config.tests.ConfigurationFactoryTest)  Time elapsed: 0 sec  <<< FAILURE!
java.lang.AssertionError: 
Expected: a string ending with "factory-test-invalid.yml has the following errors:\n  * name must match \"[\w]+[\s]+[\w]+\" (was Boop)"
     got: "/home/jbrauer/projects/dropwizard/dropwizard-core/target/test-classes/factory-test-invalid.yml has the following errors:\n  * name muss auf Ausdruck \"[\w]+[\s]+[\w]+\" passen (was Boop)"

    at org.junit.Assert.assertThat(Assert.java:780)
    at org.junit.Assert.assertThat(Assert.java:738)
    at com.yammer.dropwizard.config.tests.ConfigurationFactoryTest.throwsAnExceptionOnInvalidFiles(ConfigurationFactoryTest.java:58)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:24)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.apache.maven.surefire.junitcore.ConfigurableParallelComputer$AsynchronousRunner$1.call(ConfigurableParallelComputer.java:186)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

jbrauer@werner:~/projects/dropwizard (master)$ cat /home/jbrauer/projects/dropwizard/dropwizard-core/target/surefire-reports/com.yammer.dropwizard.validation.tests.ValidatorTest.txt
-------------------------------------------------------------------------------
Test set: com.yammer.dropwizard.validation.tests.ValidatorTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: -2,104,463.747 sec <<< FAILURE!
returnsASetOfErrorsForAnObject(com.yammer.dropwizard.validation.tests.ValidatorTest)  Time elapsed: 0.001 sec  <<< FAILURE!
java.lang.AssertionError: 
Expected: is <[notNull may not be null (was null), tooBig must be less than or equal to 30 (was 50)]>
     got: <[notNull kann nicht null sein (was null), tooBig muss kleinergleich 30 sein (was 50)]>

    at org.junit.Assert.assertThat(Assert.java:780)
    at org.junit.Assert.assertThat(Assert.java:738)
    at com.yammer.dropwizard.validation.tests.ValidatorTest.returnsASetOfErrorsForAnObject(ValidatorTest.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:24)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.apache.maven.surefire.junitcore.ConfigurableParallelComputer$AsynchronousRunner$1.call(ConfigurableParallelComputer.java:186)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

New config parser fails with nested types

DW >= 0.2.0 breaks config parsing. I have the following configuration classes for my (Scala) service:

class CryptoConfiguration extends Configuration {
  var keyStore = "key.store"
  var keyPass = ""
  var algo = ""
  var keySize: Int = 0
}

class ExternalServiceConfiguration extends Configuration {
  var api: URL = new URL("http://wrongo:1234")
  var user = ""
  var pass = ""
}

class ServiceConfiguration extends Configuration {
  var database = new DatabaseConfiguration()
  var rabbit = new RabbitConfiguration()
  var cachet = new CachetConfiguration()
  var s3 = new S3Configuration()
  var crypto = new CryptoConfiguration()
  var api = new URL("http://fixme")
}

And here's the config.yml:

http:
  port: 12004
  adminPort: 12005
  maxIdleTime: 30s

  requestLog:
    enabled: true

rabbit:
  host: localhost
  vhost: /local/myservice
  username: myservice
  password: myservice

database:
  url: jdbc:postgresql://localhost/myservice
  username: myservice
  password: myservice

externalservice:
  api: https://externalservice.com
  user: elided
  pass: elided

# Logging settings.
logging:
  level: DEBUG
  console:
    enabled: true
    threshold: DEBUG

  syslog:
    enabled: false

  loggers:
    org.apache.http.wire: WARN
    org.eclipse.jetty.http.HttpParser: WARN
    org.apache.http.headers: DEBUG
    org.eclipse.jetty.server.AbstractHttpConnection: WARN
    org.eclipse.jetty.server.handler.ContextHandler: WARN
    org.eclipse.jetty.io.nio.ChannelEndPoint: WARN
    org.eclipse.jetty.servlet.ServletHandler: WARN
    org.eclipse.jetty.server.AbstractConnector: WARN
    org.eclipse.jetty.util.component.AbstractLifeCycle: WARN
    org.eclipse.jetty.util.component.Container: WARN

  file:
    enabled: true
    threshold: ALL
    filenamePattern: ./logs/myservice.log

s3:
  bucket: myservice-dev
  key: elidedd
  secret: elided

crypto:
  keyStore: key.store
  keyPass: elided
  algo: Blowfish
  keySize: 256

api: http://randomhost

When I start the service, it dies with:

Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "rabbit" (Class com.simple.clownshoes.config.ServiceConfiguration), not marked as ignorable
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.simple.clownshoes.config.ServiceConfiguration["rabbit"])
    at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
    at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:672)
    at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:658)
    at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1361)
    at org.codehaus.jackson.map.deser.BeanDeserializer._handleUnknown(BeanDeserializer.java:725)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:703)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
    at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1999)
    at com.yammer.dropwizard.json.Json.readYamlValue(Json.java:508)
    at com.yammer.dropwizard.config.ConfigurationFactory.parse(ConfigurationFactory.java:43)
    at com.yammer.dropwizard.config.ConfigurationFactory.build(ConfigurationFactory.java:36)
    at com.yammer.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:69)
    at com.yammer.dropwizard.cli.Command.run(Command.java:112)
    at com.yammer.dropwizard.AbstractService.run(AbstractService.java:178)
    at com.yammer.dropwizard.ScalaService.main(ScalaService.scala:13)

Everything works fine on 0.1.3.

Programmatic overrides to Config: private to protected or mutator(s)?

I am trying to change default settings for some of DropWizard Configuration settings (specifically, disabling default gzip handling to customize it). This seemed simple: in default constructor of my sub-class I just tweak properties.

But the snatch is that properties are typically declared as private, and there are no mutators.
My first inclination is to change properties to protected, so that my sub-class can change their values.

Alternatively I could add mutator(s), but this seems bit more convoluted. And changing access to protected would also make it possible for sub-classes to add mutators by those who... like, like adding setXxx() methods.

If this does not sounds like a colossally bad idea, I'll submit a patch for this.

Ability to change the system property prefix

Currently in ConfigurationFactory the system property prefix is set to "dw." and cannot be changed. I would very much like to be able to change this.

Would it be possible to add this feature? Would it be accepted?

Allow for command-line overrides of configuration parameters

As @BradHouse said on the mailing list:

I reguarly launch services with Foreman and usually launch multiple instances at the same time. Each instance needs a different port.

A more serious issue is if you want to run a DW service on something like Heroku. You must bind on the port they define via the $PORT environment variable. If you cannot pass this on the CLI you cannot run on their service. System.getenv("PORT") also works, but there are no bind points in DW to alter a Configuration instance prior to Jetty start. Not surprising as the DW approach strongly favours immutable configuration.

dropwizard-view: Missing Template error

Getting the Missing Template error when I place the template in classpath relative to the View class, e.g. src/main/resources/my/package/ViewClass/view.ftl
I was able to resolve it finally by placing the view.ftl in src/main/resources (root of the classpath)

While debugging the template I encounter following in ViewMessageBodyWriter.java:
38: configuration.setClassForTemplateLoading(key, "/");

If I change this invocation to configuration.setClassForTemplateLoading(key, ""), things work almost as documented, i.e. the template file is picked up from: src/main/resources/my/package/view.ftl (I have also found that this may, in fact, be preferable to .../my/package/View/view.ftl since eclipse flags a warning on the View class "The type View collides with a package")

Build fails

Just tried to build from latest GIT, but HttpClient support fails:


T E S T S

Running com.yammer.metrics.httpclient.tests.InstrumentedHttpClientTest
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.247 sec
Running com.yammer.metrics.httpclient.tests.InstrumentedClientConnManagerTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.023 sec <<< FAILURE!

Results :

Failed tests:
what(com.yammer.metrics.httpclient.tests.InstrumentedClientConnManagerTest):
Expected: is 1
got: 0

Rationalize object lifecycles

Because so much of Dropwizard is glue code, the way in which things are created, configured, and wired together could use a bit more love.

  • Jersey servlet container
    • enable/disable Jersey features
    • swap out for a GuiceContainer or what have you
  • Jackson factory/mapper
    • add modules, etc. for ConfigurationFactory and JacksonMessageBodyProvider
    • enable/disable features for specific instances (e.g., ConfigurationFactory needs to always reject unknown fields)
  • MetricsRegistry/HealthCheckRegistry
    • really starting to regret those static factory methods
    • would make JRebel-style development reloads possible

AbstractService#getJerseyContainer needs to add a Configuration parameter

There are scenarios in which getJerseyContainer will need to read the Service's com.yammer.dropwizard.config.Configuration but it has no way of accessing it.

The method signature can be changed to

public ServletContainer getJerseyContainer( DropwizardResourceConfig resourceConfig, T serviceConfig )

or DropwizardResourceConfig can be changed so that it has a publicly accessible reference to the service config.

The use-case is as follows:

When using Guice, a service will override getJerseyContainer to return com.sun.jersey.guice.spi.container.servlet.GuiceContainer, which needs an initialized Injector. Initializing the injector can depend on the service's Configuration object.

DropWizard should automatically log resource exceptions

DropWizard is awesome! Thanks for such a productivity booster! Even so, I found a little gotcha to share...

DropWizard should automatically log exceptions to console without the user needing to manually insert try-catch-log boilerplate to every resource method. This would reduce plumbing and improve usability.

I have the following gcwizard.yml config file:
logging:

Settings for logging to stdout.

console:

# If true, write log statements to stdout.
enabled: true

# Do not display log statements below this threshold to stdout.
threshold: TRACE

… and there's a server resource with a simple method like so:

@get
@timed
@ExceptionMetered
public Saying sayHello(@QueryParam("name") Optional name) {
// try {
dumpMetrics();
return new Saying(counter.incrementAndGet(), String.format(template, name.or(defaultName)));
// } catch (Throwable e) {
// LOG.error(e, "gcwizard error in sayHello");
// throw new RuntimeException(e);
// }
}

When I call the service curl tells me that something failed, but it turns out the dropwizard service didn't log any stacktrace:

$ curl http://localhost:8080/gcwizard?name=foo
There was an error processing your request. It has been logged (ID 46b4c2d7ea57bef0).

DropWizard console output isn't very helpful to diagnose the problem:

INFO [2012-06-11 01:23:54,919] org.eclipse.jetty.server.AbstractConnector: Started [email protected]:8080
INFO [2012-06-11 01:23:54,923] org.eclipse.jetty.server.AbstractConnector: Started [email protected]:8081
0:0:0:0:0:0:0:1%0 - - [11/Jun/2012:01:23:57 +0000] "GET /gcwizard?name=foo HTTP/1.1" 400 72 2152 2152

Now, if I uncomment the try-catch-LOG block above and rerun I can see the stacktrace to diagnose the problem (see output further below). With this stacktrace I was easily able to fix my bug in calling some third party REST service (fix was to ask jackson to map JSON to Map.class instead of String.class). However, I believe I shouldn't have to manually add that try-catch-log boilerplate to every resource method since the doc at http://dropwizard.codahale.com/manual/core/#logging nicely promises to already do this:

"Error Handling :If your resource class unintentionally throws an exception, Dropwizard will log that exception (including stack traces) and return a terse, safe text/plain 500 Internal Server Error response."

What am I missing?

FYI, here is what my manual try-catch-log statement prints:


INFO [2012-06-11 01:22:54,639] org.eclipse.jetty.server.AbstractConnector: Started [email protected]:8080
INFO [2012-06-11 01:22:54,642] org.eclipse.jetty.server.AbstractConnector: Started [email protected]:8081
ERROR [2012-06-11 01:22:58,720] com.alpinefex.gcwizard.GCWizardResource: gcwizard error in sayHello
! javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:103)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:82)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:554)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
! at com.sun.jersey.api.client.WebResource.handle(WebResource.java:684)
! at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
! at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
! at com.alpinefex.gcwizard.GCWizardResource.dumpMetrics(GCWizardResource.java:84)
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:72)
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
! at java.lang.reflect.Method.invoke(Method.java:597)
! at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
! at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
! at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$TimedRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:34)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$ExceptionMeteredRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:73)
! at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
! at com.sun.jersey.server.impl.uri.rules.ResourceObjectRule.accept(ResourceObjectRule.java:100)
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
! at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
! at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
! at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
! at com.yammer.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:47)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1367)
! at com.yammer.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:29)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:484)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at com.yammer.metrics.jetty.InstrumentedHandler.handle(InstrumentedHandler.java:200)
! at org.eclipse.jetty.server.handler.GzipHandler.handle(GzipHandler.java:270)
! at com.yammer.dropwizard.jetty.BiDiGzipHandler.handle(BiDiGzipHandler.java:123)
! at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at org.eclipse.jetty.server.Server.handle(Server.java:350)
! at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
! at org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:47)
! at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
! at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944)
! at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630)
! at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
! at org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:66)
! at org.eclipse.jetty.server.nio.BlockingChannelConnector$BlockingChannelEndPoint.run(BlockingChannelConnector.java:293)
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
! at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
! at java.lang.Thread.run(Thread.java:680)
Caused by: ! org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
! at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
! at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
! at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1923)
! at com.yammer.dropwizard.json.Json.readValue(Json.java:335)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:101)
!... 56 common frames omitted
ERROR [2012-06-11 01:22:58,722] com.yammer.dropwizard.jersey.LoggingExceptionMapper: Error handling a request: 46b4c2d7ea57bef0
! java.lang.RuntimeException: javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:76)
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
! at java.lang.reflect.Method.invoke(Method.java:597)
! at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
! at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
! at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$TimedRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:34)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$ExceptionMeteredRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:73)
! at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
! at com.sun.jersey.server.impl.uri.rules.ResourceObjectRule.accept(ResourceObjectRule.java:100)
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
! at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
! at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
! at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
! at com.yammer.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:47)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1367)
! at com.yammer.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:29)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:484)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at com.yammer.metrics.jetty.InstrumentedHandler.handle(InstrumentedHandler.java:200)
! at org.eclipse.jetty.server.handler.GzipHandler.handle(GzipHandler.java:270)
! at com.yammer.dropwizard.jetty.BiDiGzipHandler.handle(BiDiGzipHandler.java:123)
! at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at org.eclipse.jetty.server.Server.handle(Server.java:350)
! at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
! at org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:47)
! at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
! at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944)
! at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630)
! at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
! at org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:66)
! at org.eclipse.jetty.server.nio.BlockingChannelConnector$BlockingChannelEndPoint.run(BlockingChannelConnector.java:293)
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
! at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
! at java.lang.Thread.run(Thread.java:680)
Caused by: ! javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:103)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:82)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:554)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
! at com.sun.jersey.api.client.WebResource.handle(WebResource.java:684)
! at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
! at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
! at com.alpinefex.gcwizard.GCWizardResource.dumpMetrics(GCWizardResource.java:84)
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:72)
!... 48 common frames omitted
Caused by: ! org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
! at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
! at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
! at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1923)
! at com.yammer.dropwizard.json.Json.readValue(Json.java:335)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:101)
!... 56 common frames omitted
0:0:0:0:0:0:0:1%0 - - [11/Jun/2012:01:22:56 +0000] "GET /gcwizard?name=foo HTTP/1.1" 500 86 2587 2587

JackonMessageBodyProvider-raised exceptions aren't logged in LoggingExceptionMapper when called from JerseyClient

From #109:

ERROR [2012-06-11 01:22:58,720] com.alpinefex.gcwizard.GCWizardResource: gcwizard error in sayHello
! javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:103)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:82)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:554)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
! at com.sun.jersey.api.client.WebResource.handle(WebResource.java:684)
! at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
! at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
! at com.alpinefex.gcwizard.GCWizardResource.dumpMetrics(GCWizardResource.java:84)
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:72)
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
! at java.lang.reflect.Method.invoke(Method.java:597)
! at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
! at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
! at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$TimedRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:34)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$ExceptionMeteredRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:73)
! at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
! at com.sun.jersey.server.impl.uri.rules.ResourceObjectRule.accept(ResourceObjectRule.java:100)
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
! at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
! at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
! at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
! at com.yammer.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:47)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1367)
! at com.yammer.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:29)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:484)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at com.yammer.metrics.jetty.InstrumentedHandler.handle(InstrumentedHandler.java:200)
! at org.eclipse.jetty.server.handler.GzipHandler.handle(GzipHandler.java:270)
! at com.yammer.dropwizard.jetty.BiDiGzipHandler.handle(BiDiGzipHandler.java:123)
! at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at org.eclipse.jetty.server.Server.handle(Server.java:350)
! at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
! at org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:47)
! at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
! at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944)
! at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630)
! at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
! at org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:66)
! at org.eclipse.jetty.server.nio.BlockingChannelConnector$BlockingChannelEndPoint.run(BlockingChannelConnector.java:293)
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
! at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
! at java.lang.Thread.run(Thread.java:680)
Caused by: ! org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
! at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
! at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
! at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1923)
! at com.yammer.dropwizard.json.Json.readValue(Json.java:335)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:101)
!... 56 common frames omitted
ERROR [2012-06-11 01:22:58,722] com.yammer.dropwizard.jersey.LoggingExceptionMapper: Error handling a request: 46b4c2d7ea57bef0
! java.lang.RuntimeException: javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:76)
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
! at java.lang.reflect.Method.invoke(Method.java:597)
! at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
! at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
! at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$TimedRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:34)
! at com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchProvider$ExceptionMeteredRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:73)
! at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
! at com.sun.jersey.server.impl.uri.rules.ResourceObjectRule.accept(ResourceObjectRule.java:100)
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
! at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
! at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
! at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
! at com.yammer.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:47)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1367)
! at com.yammer.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:29)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:484)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at com.yammer.metrics.jetty.InstrumentedHandler.handle(InstrumentedHandler.java:200)
! at org.eclipse.jetty.server.handler.GzipHandler.handle(GzipHandler.java:270)
! at com.yammer.dropwizard.jetty.BiDiGzipHandler.handle(BiDiGzipHandler.java:123)
! at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
! at org.eclipse.jetty.server.Server.handle(Server.java:350)
! at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
! at org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:47)
! at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
! at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944)
! at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630)
! at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
! at org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:66)
! at org.eclipse.jetty.server.nio.BlockingChannelConnector$BlockingChannelEndPoint.run(BlockingChannelConnector.java:293)
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
! at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
! at java.lang.Thread.run(Thread.java:680)
Caused by: ! javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:103)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:82)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:554)
! at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
! at com.sun.jersey.api.client.WebResource.handle(WebResource.java:684)
! at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
! at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
! at com.alpinefex.gcwizard.GCWizardResource.dumpMetrics(GCWizardResource.java:84)
! at com.alpinefex.gcwizard.GCWizardResource.sayHello(GCWizardResource.java:72)
!... 48 common frames omitted
Caused by: ! org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: com.sun.jersey.client.apache4.ApacheHttpClient4Handler$HttpClientResponseInputStream@3c9ed91f; line: 1, column: 1]
! at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
! at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
! at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
! at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
! at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1923)
! at com.yammer.dropwizard.json.Json.readValue(Json.java:335)
! at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.parseEntity(JacksonMessageBodyProvider.java:101)
!... 56 common frames omitted

Support multiple config files

We'd find it really useful to be able to specify two config files at startup, one for defaults, one for environment-specific overrides.

Would you be interested in including this if we supply a patch?

Scala resources returning objects don't work with 0.1.3

Here's a resource

package com.scala.aboutdropwizard                                                                                                                                                                                                                                                                                                                                                                                           

import javax.ws.rs.core.MediaType                                                                                                                                                                                                                                                                                                                                                                                                     
import javax.ws.rs.{GET, Produces, Path}      


case class DumbResult(val itIsOk: Boolean)                                                                                                                                                                                                                                                                                                                                                                                            

@Path("/hello-scala-world")                                                                                                                                                                                                                                                                                                                                                                                                           
@Produces(Array(MediaType.APPLICATION_JSON))                                                                                                                                                                                                                                                                                                                                                                                          
class HelloScalaWorldResource {                                                                                                                                                                                                                                                                                                                                                                                                       

  @GET def wellHello(): DumbResult =  {                                                                                                                                                                                                                                                                                                                                                                                               
    DumbResult(true)                                                                                                                                                                                                                                                                                                                                                                                                                  
  }                                                                                                                                                                                                                                                                                                                                                                                                                                   
}                  

And a scala service:

package com.scala.aboutdropwizard                                                                                                                                                                                                                                                                                                                                                                                           

import scala.collection.mutable.ArrayBuilder                                                                                                                                                                                                                                                                                                                                                                                          

import com.aboutdropwizard._                                                                                                                                                                                                                                                                                                                                                                                                

import com.yammer.dropwizard.Service                                                                                                                                                                                                                                                                                                                                                                                                  
import com.yammer.dropwizard.config.Environment                                                                                                                                                                                                                                                                                                                                                                                       

class HelloScalaWorldService extends Service[HelloWorldConfiguration]("The scala hello world") {                                                                                                                                                                                                                                                                                                                                      

  def initialize(config: HelloWorldConfiguration, environment: Environment) {                                                                                                                                                                                                                                                                                                                                                         

    val template = config.getTemplate                                                                                                                                                                                                                                                                                                                                                                                                 
    val defaultName = config.getDefaultName                                                                                                                                                                                                                                                                                                                                                                                           

    environment.addResource(new HelloWorldResource(template, defaultName));                                                                                                                                                                                                                                                                                                                                                           
    environment.addResource(new HelloScalaWorldResource)                                                                                                                                                                                                                                                                                                                                                                              
    environment.addHealthCheck(new TemplateHealthCheck(template));                                                                                                                                                                                                                                                                                                                                                                    
  }                                                                                                                                                                                                                                                                                                                                                                                                                                   
}                                                                                                                                                                                                                                                                                                                                                                                                                                     

object HelloScalaWorldService {                                                                                                                                                                                                                                                                                                                                                                                                       
  def main(args: Array[String]) = new HelloScalaWorldService().run(args)                                                                                                                                                                                                                                                                                                                                                              
}                         

Doing curl -v http://127.0.0.1:8080/hello-scala-world

produces the following error:

ERROR [2012-01-25 00:11:33,141] com.sun.jersey.spi.container.ContainerResponse: A message body writer for Java class com.scala.aboutdropwizard.DumbResult, and Java type class com.scala.aboutdropwizard.DumbResult, and MIME media type application/json was not found
ERROR [2012-01-25 00:11:33,142] com.sun.jersey.spi.container.ContainerResponse: The registered message body writers compatible with the MIME media type are:

application/json ->
  com.yammer.dropwizard.jersey.JacksonMessageBodyProvider
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General

The java version of the resource works fine.

Using @produces(MediaType.APPLICATION_JSON) in scala instead of @produces(Array(MediaType.APPLICATION_JSON)) produces compile-time error

[ERROR]  found   : java.lang.String("application/json")
[ERROR]  required: Array[java.lang.String]
[ERROR] @Produces(MediaType.APPLICATION_JSON)

I'm at a loss w.r.t. what's going on. Any suggestions?

Support SSL and certificates for client authentication

I noticed a pull request get merged a couple weeks ago that adds SSL termination within Drop Wizard. One of the big advantages of performing termination within Jetty (rather than in a fronting proxy) is having direct access to information about the client certificate. It would be great to have an authentication mechanism that returns the DN (or part thereof) from a validated client certificate.

Certificate authentication is a fairly widespread requirement for APIs, especially ones running over HTTP(S). I'd be happy to take a stab at implementing this if I can get some guidance about where such an implementation would go. (I'm very new to Drop Wizard.)

Routes display bug during startup

I have a class defined like:

@Path("/locations")
@Produces(Array(MediaType.APPLICATION_JSON + ";charset=utf-8"))
class LocationsResource(httpClient:HttpClient){
  @GET
  @Path("/search")
  @Timed
  def search(@QueryParam("ll") ll:String, @QueryParam("q") query:Option[String]):Response = {
  }
  @GET
  @Path("/details")
  @Timed
  def details(@QueryParam("venue_id") VenueIdOpt:Option[String]):Response = {
  }
}

The routes display:

    GET     /locations (xxx.LocationsResource)
    GET     /locations (xxx.LocationsResource)

When it should show the path for each method: GET /locations/search

writesMustacheViews test fails on windows

Test input file yay.mustache uses LF while the expected string evaluates to CRLF under windows hence AssertionError:

Expected: is "Hello Stranger!\r\nWoo!\r\n\r\n"
     got: "Hello Stranger!\nWoo!\n\n"

I think the String.format should also use LF i.e.

String.format("Hello Stranger!\nWoo!\n\n")

TaskServlet should return a Content-Type

The TaskServlet doesn't return a content type, which begs the question…what content type should the Tasks be writing to the PrintWriter? Tasks don't have any control over the Content-Type, indicating that dropwizard is in control of that. But there's no indication of what they ought to be sending back (text/plain? text/html? text/xml? application/json?).

Can not access admin tasks when running application and admin contexts on same port.

There are issues with admin tasks when running the application and admin contexts off the same port. For example:

curl -X POST http://localhost:8081/tasks/build-index

works great. But if I use the same port in configuration yaml:
http:
port: 8080
adminPort: 8080

curl -X POST http://localhost:8080/admin/tasks/build-index

gives me a 404

I know that the admin context path is correct because my health checks work:
curl http://localhost:8080/admin/healthcheck

  • dao: OK
  • deadlocks: OK

How to reload static assets in dev mode?

Since Dropwizard supports AssetsBundles, I thought that maybe this would be a quick way to test out them fancy JS frontends (Backbone, Ember, etc.) by throwing them up on src/main/resources/assets and trying to serve from there. However, I quickly realized that Dropwizard either doesn't reload any of the resources it gets, or I'm missing something on configuring it to reload static assets.

If I were using the Maven Jetty plugin, I'd throw something in the config to use a custom webdefault.xml file as described here for us lowly Windows users: http://docs.codehaus.org/display/JETTY/Files+locked+on+Windows

Is this something I can do in Dropwizard, or is this too far out of scope for the tool?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.