From 56ae3b83a4159c7156dadb59f6017fa06a390d25 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 27 Apr 2011 15:05:09 -0700 Subject: [PATCH 01/25] Bump version. --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e3a00b17913..484ef2fbafd 100644 --- a/project/build.properties +++ b/project/build.properties @@ -3,6 +3,6 @@ project.organization=com.yammer project.name=dropwizard sbt.version=0.7.5.RC0 -project.version=0.0.3 +project.version=0.0.4-SNAPSHOT build.scala.versions=2.8.1 project.initialize=false From 1c1a3d43f6c645895bdec5fa93e4ba81366c9cc0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 00:43:30 -0700 Subject: [PATCH 02/25] Radical overhaul. Not even gonna try to make this a series of commits. Sorry. --- project/build/DropwizardProject.scala | 12 +-- .../com/yammer/dropwizard/Environment.scala | 64 +++++++++++++ .../scala/com/yammer/dropwizard/Service.scala | 29 ++---- .../com/yammer/dropwizard/cli/Command.scala | 18 +--- .../dropwizard/cli/ConfiguredCommand.scala | 12 +-- .../dropwizard/cli/ManagedCommand.scala | 14 +-- .../yammer/dropwizard/cli/ServerCommand.scala | 45 +++------- .../config/ConfigurationFactory.scala | 52 +++++++++++ .../RequestLogHandlerFactory.scala} | 16 +--- .../dropwizard/config/ServerFactory.scala | 83 +++++++++++++++++ .../jersey/ScanningGuiceContainer.scala | 24 ----- .../dropwizard/lifecycle/JettyManager.scala | 23 ++--- .../modules/ConfigurationModule.scala | 49 ---------- .../dropwizard/modules/GuiceModule.scala | 25 ------ .../modules/GuiceMultibindingModule.scala | 11 --- .../modules/GuiceServletModule.scala | 31 ------- .../modules/JerseyServletModule.scala | 11 --- .../dropwizard/modules/ProviderModule.scala | 9 -- .../dropwizard/modules/ServerModule.scala | 89 ------------------- .../LoggingExceptionMapper.scala | 2 +- .../yammer/dropwizard/service/Jersey.scala | 10 --- .../yammer/dropwizard/examples/Example.scala | 18 ++-- .../examples/HelloWorldResource.scala | 4 +- .../dropwizard/examples/SayCommand.scala | 9 +- .../dropwizard/examples/SayingFactory.scala | 7 ++ .../dropwizard/examples/SayingModule.scala | 16 ---- .../dropwizard/examples/SplodyCommand.scala | 11 +-- .../dropwizard/examples/StartableObject.scala | 3 +- 28 files changed, 279 insertions(+), 418 deletions(-) create mode 100644 src/main/scala/com/yammer/dropwizard/Environment.scala create mode 100644 src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala rename src/main/scala/com/yammer/dropwizard/{modules/RequestLogHandlerModule.scala => config/RequestLogHandlerFactory.scala} (74%) create mode 100644 src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/jersey/ScanningGuiceContainer.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/ConfigurationModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/GuiceModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/GuiceMultibindingModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/GuiceServletModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/JerseyServletModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/ProviderModule.scala delete mode 100644 src/main/scala/com/yammer/dropwizard/modules/ServerModule.scala rename src/main/scala/com/yammer/dropwizard/{jersey => providers}/LoggingExceptionMapper.scala (95%) delete mode 100644 src/main/scala/com/yammer/dropwizard/service/Jersey.scala create mode 100644 src/test/scala/com/yammer/dropwizard/examples/SayingFactory.scala delete mode 100644 src/test/scala/com/yammer/dropwizard/examples/SayingModule.scala diff --git a/project/build/DropwizardProject.scala b/project/build/DropwizardProject.scala index e33cdcd372e..74d264dcb88 100644 --- a/project/build/DropwizardProject.scala +++ b/project/build/DropwizardProject.scala @@ -31,20 +31,10 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) val simplespec = "com.codahale" %% "simplespec" % "0.2.0" % "test" val mockito = "org.mockito" % "mockito-all" % "1.8.4" % "test" - /** - * Guice Dependencies - */ - val guice = "com.google.inject" % "guice" % "3.0" - val guiceServlet = "com.google.inject.extensions" % "guice-servlet" % "3.0" - val guiceMultibindings = "com.google.inject.extensions" % "guice-multibindings" % "3.0" - /** * Jersey Dependencies */ val jerseyScala = "com.codahale" %% "jersey-scala" % "0.1.3" - // TODO: 3/29/11 -- Change back to regular packaging once - // http://java.net/jira/browse/JERSEY-697 is resolved. - val jerseyGuice = "com.sun.jersey.contribs" % "jersey-guice-nogrizzly" % "1.6" /** * Misc Dependencies @@ -53,6 +43,8 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) val jerkson = "com.codahale" %% "jerkson" % "0.1.7" val metrics = "com.yammer" %% "metrics" % "2.0.0-BETA11" val commonsCli = "commons-cli" % "commons-cli" % "1.2" + val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % "1.7.5" + val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.7.5" /** * Logging Dependencies diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala new file mode 100644 index 00000000000..08a4f434063 --- /dev/null +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -0,0 +1,64 @@ +package com.yammer.dropwizard + +import lifecycle.Managed +import com.sun.jersey.api.core.ResourceConfig._ +import com.codahale.logula.Logging +import com.sun.jersey.api.core.DefaultResourceConfig +import com.yammer.metrics.HealthChecks +import com.yammer.metrics.core.{DeadlockHealthCheck, HealthCheck} + +class Environment extends DefaultResourceConfig with Logging { + private[dropwizard] var resources = Set.empty[Object] + private[dropwizard] var healthChecks = Set.empty[HealthCheck] + private[dropwizard] var providers = Set.empty[Object] + private[dropwizard] var managedObjects = IndexedSeq.empty[Managed] + + def addResource(resource: Object) { + if (!isRootResourceClass(resource.getClass)) { + throw new IllegalArgumentException(resource.getClass.getCanonicalName + + " is not a @Path-annotated resource class") + } + resources += resource + getSingletons.add(resource) + } + + def addProvider(provider: Object) { + if (!isProviderClass(provider.getClass)) { + throw new IllegalArgumentException(provider.getClass.getCanonicalName + + " is not a @Provider-annotated provider class") + } + providers += provider + getSingletons.add(provider) + } + + def addHealthCheck(healthCheck: HealthCheck) { + healthChecks += healthCheck + HealthChecks.register(healthCheck) + } + + def manage(managedObject: Managed) { + managedObjects ++= IndexedSeq(managedObject) + } + + override def validate() { + super.validate() + + log.info("resources = %s", resources.mkString("{", ", ", "}")) + log.info("providers = %s", providers.mkString("{", ", ", "}")) + log.info("health checks = %s", healthChecks.mkString("{", ", ", "}")) + log.info("managed objects = %s", managedObjects.mkString("{", ", ", "}")) + + if (healthChecks.isEmpty) { + log.warn(""" + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! THIS SERVICE HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW IF IT ! +! DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE LETTING ! +! YOUR USERS DOWN. ADD HEALTHCHECKS OR FEAR THE WRATH OF THE DROP WIZARD. ! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +""") + } + } +} diff --git a/src/main/scala/com/yammer/dropwizard/Service.scala b/src/main/scala/com/yammer/dropwizard/Service.scala index 24209be4ae9..514af3d8d5d 100644 --- a/src/main/scala/com/yammer/dropwizard/Service.scala +++ b/src/main/scala/com/yammer/dropwizard/Service.scala @@ -1,40 +1,22 @@ package com.yammer.dropwizard -import collection.{immutable, mutable} -import lifecycle.Managed -import modules.{GuiceMultibindingModule, ServerModule, RequestLogHandlerModule} +import collection.immutable import util.JarAware import com.codahale.logula.Logging -import com.google.inject.Module import com.yammer.dropwizard.cli.{ServerCommand, Command} -import com.yammer.metrics.guice.InstrumentationModule -import com.yammer.metrics.core.{DeadlockHealthCheck, HealthCheck} +import com.codahale.fig.Configuration trait Service extends Logging with JarAware { - private val modules = new mutable.ArrayBuffer[Module]() ++ Seq( - new RequestLogHandlerModule, - new ServerModule, - new InstrumentationModule - ) - protected def require(modules: Module*) { this.modules ++= modules } - private var commands = new immutable.TreeMap[String, Command] protected def provide(commands: Command*) { commands.foreach { c => this.commands += c.name -> c } } provide(new ServerCommand(this)) - - protected def healthCheck[A <: HealthCheck](implicit mf: Manifest[A]) { - modules += new GuiceMultibindingModule(classOf[HealthCheck], mf.erasure.asInstanceOf[Class[HealthCheck]]) - } - healthCheck[DeadlockHealthCheck] - - protected def manage[A <: Managed](implicit mf: Manifest[A]) { - modules += new GuiceMultibindingModule(classOf[Managed], mf.erasure.asInstanceOf[Class[Managed]]) - } def name: String def banner: Option[String] = None + def configure(config: Configuration, environment: Environment) + private def printUsage(error: Option[String] = None) { for (msg <- error) { System.err.printf("%s\n\n", msg) @@ -42,7 +24,6 @@ trait Service extends Logging with JarAware { printf("%s [arg1 arg2]\n\n", jarSyntax) - println("Commands") println("========\n") for (cmd <- commands.values) { @@ -57,7 +38,7 @@ trait Service extends Logging with JarAware { case command :: args => { commands.get(command) match { case Some(cmd) => { - cmd.execute(jarSyntax, modules.toSeq, args) + cmd.execute(this, jarSyntax, args) } case None => printUsage(Some("Unrecognized command: " + command)) } diff --git a/src/main/scala/com/yammer/dropwizard/cli/Command.scala b/src/main/scala/com/yammer/dropwizard/cli/Command.scala index 8f700e8d2b1..6395a3326cc 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/Command.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/Command.scala @@ -1,14 +1,9 @@ package com.yammer.dropwizard.cli -import collection.mutable -import com.google.inject._ import org.apache.commons.cli.{ParseException, GnuParser, Options, HelpFormatter, Option => ApacheOption, OptionGroup} +import com.yammer.dropwizard.Service trait Command { - private val modules = new mutable.ArrayBuffer[Module] - - protected def require(modules: Module*) {this.modules ++= modules} - def name: String def description: Option[String] = None @@ -32,10 +27,8 @@ trait Command { opts } - def execute(jarSyntax: String, modules: Seq[Module], args: Seq[String]) { + def execute(service: Service, jarSyntax: String, args: Seq[String]) { try { - this.modules ++= modules - if (args == Seq("-h") || args == Seq("--help")) { printUsage(jarSyntax, None) } else { @@ -46,7 +39,7 @@ trait Command { Option(o.getValues).getOrElse(Array.empty[String]).toList }.toMap - run(opts, cmdLine.getArgs.toList).foreach { e => + run(service, opts, cmdLine.getArgs.toList).foreach { e => printUsage(jarSyntax, Some(e)) } } @@ -54,20 +47,17 @@ trait Command { case e: ParseException => { printUsage(jarSyntax, Some(e.getMessage)) } - case e: CreationException => System.err.println(e.getMessage) case e => { e.printStackTrace() } } } - def run(opts: Map[String, List[String]], args: List[String]): Option[String] + def run(service: Service, opts: Map[String, List[String]], args: List[String]): Option[String] protected def commandSyntax(jarSyntax: String) = "%s %s [options] [arguments]".format(jarSyntax, name) - protected lazy val injector = Guice.createInjector(Stage.PRODUCTION, modules: _*) - def printUsage(jarSyntax: String, error: Option[String] = None) { for (msg <- error) { System.err.printf("%s\n\n", msg) diff --git a/src/main/scala/com/yammer/dropwizard/cli/ConfiguredCommand.scala b/src/main/scala/com/yammer/dropwizard/cli/ConfiguredCommand.scala index 153b0c9c038..08bb9b018f5 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/ConfiguredCommand.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/ConfiguredCommand.scala @@ -2,20 +2,22 @@ package com.yammer.dropwizard.cli import java.io.File import com.codahale.jerkson.ParsingException -import com.yammer.dropwizard.modules.ConfigurationModule import com.codahale.logula.Logging +import com.yammer.dropwizard.config.ConfigurationFactory +import com.codahale.fig.Configuration +import com.yammer.dropwizard.Service trait ConfiguredCommand extends Command with Logging { override protected def commandSyntax(jarSyntax: String) = "%s %s [options] [argumemts]".format(jarSyntax, name) - final def run(opts: Map[String, List[String]], args: List[String]) = args match { + final def run(service: Service, opts: Map[String, List[String]], args: List[String]) = args match { case filename :: others => { val f = new File(filename) if (f.exists && f.isFile) { try { - require(new ConfigurationModule(filename)) - runWithConfigFile(opts, others) + val config = ConfigurationFactory.buildConfiguration(filename) + run(service, config, opts, others) } catch { case e: ParsingException => Some("Bad configuration file: " + e.getMessage) case e => Some("Error: " + e.getMessage) @@ -27,5 +29,5 @@ trait ConfiguredCommand extends Command with Logging { case Nil => Some("no configuration file specified") } - def runWithConfigFile(opts: Map[String, List[String]], args: List[String]): Option[String] + def run(service: Service, config: Configuration, opts: Map[String, List[String]], args: List[String]): Option[String] } diff --git a/src/main/scala/com/yammer/dropwizard/cli/ManagedCommand.scala b/src/main/scala/com/yammer/dropwizard/cli/ManagedCommand.scala index 11402a75fc5..8e8d5d45662 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/ManagedCommand.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/ManagedCommand.scala @@ -1,19 +1,23 @@ package com.yammer.dropwizard.cli import org.eclipse.jetty.util.component.AggregateLifeCycle +import com.codahale.fig.Configuration +import com.yammer.dropwizard.{Environment, Service} import com.yammer.dropwizard.lifecycle.JettyManager -trait ManagedCommand extends Command { - final def run(opts: Map[String, List[String]], args: List[String]) = { +trait ManagedCommand extends ConfiguredCommand { + final def run(service: Service, config: Configuration, opts: Map[String, List[String]], args: List[String]): Option[String] = { val aggregate = new AggregateLifeCycle - JettyManager.collect(injector).foreach(aggregate.addBean) + val env = new Environment + service.configure(config, env) + env.managedObjects.map {new JettyManager(_)}.foreach(aggregate.addBean) aggregate.start() try { - runWithManagement(opts, args) + run(opts, args) } finally { aggregate.stop() } } - def runWithManagement(opts: Map[String, List[String]], args: List[String]): Option[String] + def run(opts: Map[String, List[String]], args: List[String]): Option[String] } diff --git a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala index 51090788d34..62fd986302a 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala @@ -1,11 +1,11 @@ package com.yammer.dropwizard.cli -import collection.JavaConversions._ -import org.eclipse.jetty.server.Server -import com.yammer.dropwizard.Service import com.yammer.metrics.HealthChecks -import com.yammer.metrics.core.HealthCheck -import com.google.inject.{ConfigurationException, Key, TypeLiteral} +import com.codahale.fig.Configuration +import com.yammer.dropwizard.config.ServerFactory +import com.yammer.metrics.core.DeadlockHealthCheck +import com.sun.jersey.spi.container.servlet.ServletContainer +import com.yammer.dropwizard.{Environment, Service} import com.yammer.dropwizard.lifecycle.JettyManager class ServerCommand(service: Service) extends ConfiguredCommand { @@ -17,40 +17,21 @@ class ServerCommand(service: Service) extends ConfiguredCommand { override def description = Some("Starts an HTTP server running the service") - private def complainAboutHealthChecks() { - log.warn(""" - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -! THIS SERVICE HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW IF IT ! -! DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE LETTING ! -! YOUR USERS DOWN. ADD HEALTHCHECKS OR FEAR THE WRATH OF THE DROP WIZARD. ! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -""") - } - - final def runWithConfigFile(opts: Map[String, List[String]], - args: List[String]) = { - try { - val healthchecks = injector.getInstance(Key.get(new TypeLiteral[java.util.Set[HealthCheck]]() {})) - healthchecks.foreach(HealthChecks.register) + final def run(service: Service, config: Configuration, opts: Map[String, List[String]], args: List[String]) = { + val env = new Environment + service.configure(config, env) + env.healthChecks.foreach(HealthChecks.register) - if (healthchecks.size == 1) { - complainAboutHealthChecks() - } - } catch { - case e: ConfigurationException => complainAboutHealthChecks() - } + val servlet = new ServletContainer(env) + val server = ServerFactory.provideServer(config, servlet) + env.managedObjects.map { new JettyManager(_) }.foreach(server.addBean) log.info("Starting %s", service.name) - service.banner.foreach {s => log.info("\n%s\n", s)} - val server = injector.getInstance(classOf[Server]) - JettyManager.collect(injector).foreach(server.addBean) server.start() server.join() + None } } diff --git a/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala new file mode 100644 index 00000000000..b9b196b0ed9 --- /dev/null +++ b/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala @@ -0,0 +1,52 @@ +package com.yammer.dropwizard.config + +import com.codahale.fig.Configuration +import java.util.logging.Logger +import org.slf4j.bridge.SLF4JBridgeHandler +import com.codahale.logula.Logging +import org.apache.log4j.Level + +object ConfigurationFactory { + def buildConfiguration(filename: String) = { + val config = new Configuration(filename) + configureLogging(config) + config + } + + private def configureLogging(config: Configuration) { + val rootLogger = Logger.getLogger("") + rootLogger.getHandlers.foreach(rootLogger.removeHandler) + rootLogger.addHandler(new SLF4JBridgeHandler) + Logging.configure { log => + log.registerWithJMX = true + + log.level = Level.toLevel(config("logging.level").or("info"), Level.INFO) + + for ((name, level) <- config("logging.loggers").asMap[String]) { + log.loggers(name.toString) = Level.toLevel(level, Level.INFO) + } + + log.console.enabled = config("logging.console.enabled").or(true) + + config("logging.console.threshold").asOption[String].foreach {l => + log.console.threshold = Level.toLevel(l, Level.ALL) + } + + if (config("logging.file.enabled").or(false)) { + log.file.enabled = true + log.file.filename = config("logging.file.filename").asRequired[String] + log.file.maxSize = config("logging.file.max_log_size_kilobytes").or(10240) + log.file.retainedFiles = config("logging.file.retain_files").or(1) + config("logging.file.threshold").asOption[String].foreach {l => + log.file.threshold = Level.toLevel(l, Level.ALL) + } + } + + if (config("logging.syslog.enabled").or(false)) { + log.syslog.enabled = true + log.syslog.host = config("logging.syslog.host").asRequired[String] + log.syslog.facility = config("logging.syslog.facility").asRequired[String] + } + } + } +} diff --git a/src/main/scala/com/yammer/dropwizard/modules/RequestLogHandlerModule.scala b/src/main/scala/com/yammer/dropwizard/config/RequestLogHandlerFactory.scala similarity index 74% rename from src/main/scala/com/yammer/dropwizard/modules/RequestLogHandlerModule.scala rename to src/main/scala/com/yammer/dropwizard/config/RequestLogHandlerFactory.scala index 6ec5fa6456c..a6387768827 100644 --- a/src/main/scala/com/yammer/dropwizard/modules/RequestLogHandlerModule.scala +++ b/src/main/scala/com/yammer/dropwizard/config/RequestLogHandlerFactory.scala @@ -1,19 +1,11 @@ -package com.yammer.dropwizard.modules +package com.yammer.dropwizard.config -import org.eclipse.jetty.server.handler.RequestLogHandler import com.codahale.fig.Configuration -import com.google.inject.{Provides, Singleton} import org.eclipse.jetty.server.NCSARequestLog +import org.eclipse.jetty.server.handler.RequestLogHandler -/** - * Given a Configuration instance, provides a RequestLogHandler instance. - * - * @author coda - */ -class RequestLogHandlerModule extends ProviderModule { - @Provides - @Singleton - def provideRequestLogHandler(config: Configuration): RequestLogHandler = { +object RequestLogHandlerFactory { + def buildHandler(implicit config: Configuration) = { val log = new NCSARequestLog log.setIgnorePaths(config("request_log.ignore_paths").asList[String].toArray) config("request_log.append").asOption[Boolean].foreach(log.setAppend) diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala new file mode 100644 index 00000000000..7cfd4a57bfd --- /dev/null +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -0,0 +1,83 @@ +package com.yammer.dropwizard.config + +import com.codahale.fig.Configuration +import com.yammer.metrics.jetty.InstrumentedHandler +import org.eclipse.jetty.server.handler.HandlerCollection +import org.eclipse.jetty.server.bio.SocketConnector +import org.eclipse.jetty.server.nio.{BlockingChannelConnector, SelectChannelConnector} +import org.eclipse.jetty.server.{Server, Connector} +import org.eclipse.jetty.util.thread.QueuedThreadPool +import com.yammer.metrics.reporting.MetricsServlet +import javax.servlet.Servlet +import org.eclipse.jetty.servlet._ + +object ServerFactory { + def provideServer(implicit config: Configuration, servlet: Servlet) = { + val server = makeServer(mainConnector, internalConnector) + + val handlers = new HandlerCollection + handlers.addHandler(new InstrumentedHandler(servletContext)) + handlers.addHandler(internalServletContext) + if (config("request_log.enabled").or(false)) { + handlers.addHandler(RequestLogHandlerFactory.buildHandler) + } + server.setHandler(handlers) + + server + } + + private def newConnector(implicit config: Configuration) = + config("http.connector").or("blocking_channel") match { + case "socket" => new SocketConnector + case "select_channel" => new SelectChannelConnector + case "blocking_channel" => new BlockingChannelConnector + } + + private def mainConnector(implicit config: Configuration) = { + val port = config("http.port").or(8080) + val connector = newConnector(config) + config("http.hostname").asOption[String].foreach(connector.setHost) + connector.setForwarded(config("http.forwarded").or(false)) + connector.setPort(port) + connector.setName("main") + connector + } + + private def makeServer(connectors: Connector*)(implicit config: Configuration) = { + val server = new Server + connectors.foreach(server.addConnector) + server.setSendServerVersion(false) + server.setThreadPool(makeThreadPool) + server.setStopAtShutdown(true) + server.setGracefulShutdown(config("http.shutdown_milliseconds").or(2000)) + server + } + + private def makeThreadPool(implicit config: Configuration) = { + val pool = new QueuedThreadPool + config("http.max_connections").asOption[Int].foreach(pool.setMaxThreads) + config("http.min_connections").asOption[Int].foreach(pool.setMinThreads) + pool + } + + private def internalConnector(implicit config: Configuration) = { + val connector = newConnector(config) + connector.setPort(config("metrics.port").or(8081)) + connector.setName("internal") + connector + } + + private def servletContext(implicit servlet: Servlet) = { + val context = new ServletContextHandler() + context.addServlet(new ServletHolder(servlet), "/") + context.setConnectorNames(Array("main")) + context + } + + private def internalServletContext = { + val internalContext = new ServletContextHandler() + internalContext.addServlet(new ServletHolder(new MetricsServlet), "/*") + internalContext.setConnectorNames(Array("internal")) + internalContext + } +} diff --git a/src/main/scala/com/yammer/dropwizard/jersey/ScanningGuiceContainer.scala b/src/main/scala/com/yammer/dropwizard/jersey/ScanningGuiceContainer.scala deleted file mode 100644 index 67617242751..00000000000 --- a/src/main/scala/com/yammer/dropwizard/jersey/ScanningGuiceContainer.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.yammer.dropwizard.jersey - -import com.google.inject.{Inject, Injector, Singleton} -import java.io.File -import com.sun.jersey.api.core.{ResourceConfig, ClasspathResourceConfig} -import com.sun.jersey.guice.spi.container.servlet.GuiceContainer -import com.sun.jersey.spi.container.servlet.WebConfig - -/** - * A Guice Jersey container which scans the entire classpath for @Path - * and @Provider annotated classes. - * - * @author coda - */ -@Singleton -class ScanningGuiceContainer @Inject() (injector: Injector) extends GuiceContainer(injector) { - override def getDefaultResourceConfig(props: java.util.Map[String, Object], webConfig: WebConfig) = { - val config = new ClasspathResourceConfig(classpath) - config.getFeatures.put(ResourceConfig.FEATURE_DISABLE_WADL, true) - config - } - - private def classpath = System.getProperty("java.class.path").split(File.pathSeparator) -} diff --git a/src/main/scala/com/yammer/dropwizard/lifecycle/JettyManager.scala b/src/main/scala/com/yammer/dropwizard/lifecycle/JettyManager.scala index 47ace859694..21f5b7bef70 100644 --- a/src/main/scala/com/yammer/dropwizard/lifecycle/JettyManager.scala +++ b/src/main/scala/com/yammer/dropwizard/lifecycle/JettyManager.scala @@ -1,22 +1,13 @@ package com.yammer.dropwizard.lifecycle -import collection.JavaConversions._ -import org.eclipse.jetty.util.component.{LifeCycle, AbstractLifeCycle} -import com.google.inject.{Key, TypeLiteral, ConfigurationException, Injector} - -object JettyManager { - def collect(injector: Injector): Seq[LifeCycle] = { - try { - val manageds = injector.getInstance(Key.get(new TypeLiteral[java.util.Set[Managed]]() {})) - manageds.map { m => new JettyManager(m) }.toSeq - } catch { - case e: ConfigurationException => Nil - } - } -} +import org.eclipse.jetty.util.component.AbstractLifeCycle class JettyManager(m: Managed) extends AbstractLifeCycle { - override def doStop() = m.stop() + override def doStop() { + m.stop() + } - override def doStart() = m.start() + override def doStart() { + m.start() + } } diff --git a/src/main/scala/com/yammer/dropwizard/modules/ConfigurationModule.scala b/src/main/scala/com/yammer/dropwizard/modules/ConfigurationModule.scala deleted file mode 100644 index 5e098a05d6a..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/ConfigurationModule.scala +++ /dev/null @@ -1,49 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.codahale.fig.Configuration -import java.util.logging.Logger -import org.slf4j.bridge.SLF4JBridgeHandler -import com.codahale.logula.Logging -import org.apache.log4j.Level - -class ConfigurationModule(filename: String) extends GuiceModule { - private val config = new Configuration(filename) - val rootLogger = Logger.getLogger("") - rootLogger.getHandlers.foreach(rootLogger.removeHandler) - rootLogger.addHandler(new SLF4JBridgeHandler) - Logging.configure { log => - log.registerWithJMX = true - - log.level = Level.toLevel(config("logging.level").or("info"), Level.INFO) - - for ((name, level) <- config("logging.loggers").asMap[String]) { - log.loggers(name.toString) = Level.toLevel(level, Level.INFO) - } - - log.console.enabled = config("logging.console.enabled").or(true) - - config("logging.console.threshold").asOption[String].foreach {l => - log.console.threshold = Level.toLevel(l, Level.ALL) - } - - if (config("logging.file.enabled").or(false)) { - log.file.enabled = true - log.file.filename = config("logging.file.filename").asRequired[String] - log.file.maxSize = config("logging.file.max_log_size_kilobytes").or(10240) - log.file.retainedFiles = config("logging.file.retain_files").or(1) - config("logging.file.threshold").asOption[String].foreach {l => - log.file.threshold = Level.toLevel(l, Level.ALL) - } - } - - if (config("logging.syslog.enabled").or(false)) { - log.syslog.enabled = true - log.syslog.host = config("logging.syslog.host").asRequired[String] - log.syslog.facility = config("logging.syslog.facility").asRequired[String] - } - } - - def configure { - bind[Configuration].toInstance(config) - } -} diff --git a/src/main/scala/com/yammer/dropwizard/modules/GuiceModule.scala b/src/main/scala/com/yammer/dropwizard/modules/GuiceModule.scala deleted file mode 100644 index e1bb6f8fea1..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/GuiceModule.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.google.inject.AbstractModule -import com.google.inject.binder.{LinkedBindingBuilder, ScopedBindingBuilder, AnnotatedBindingBuilder} -import com.google.inject.multibindings.Multibinder - -/** - * A Scala-friendly wrapper for common Guice bindings. - * - * @author coda - */ -abstract class GuiceModule extends AbstractModule { - implicit def linkedBindingBuilder2scalaBuilder[A](builder: LinkedBindingBuilder[A]) = new { - def to[T <: A](implicit mf: Manifest[T]): ScopedBindingBuilder = builder.to(mf.erasure.asInstanceOf[Class[A]]) - - def providedBy[T](implicit mf: Manifest[T]) = "poop" - } - - protected def bind[A](implicit mf: Manifest[A]): AnnotatedBindingBuilder[A] = bind(mf.erasure.asInstanceOf[Class[A]]) - - protected def multibind[A](f: Multibinder[A] => Any)(implicit mf: Manifest[A]) { - val multi = Multibinder.newSetBinder(binder, mf.erasure.asInstanceOf[Class[A]]) - f(multi) - } -} diff --git a/src/main/scala/com/yammer/dropwizard/modules/GuiceMultibindingModule.scala b/src/main/scala/com/yammer/dropwizard/modules/GuiceMultibindingModule.scala deleted file mode 100644 index 941d75f358a..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/GuiceMultibindingModule.scala +++ /dev/null @@ -1,11 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.google.inject.multibindings.Multibinder - - -class GuiceMultibindingModule[A](interface: Class[A], implementations: Class[_ <: A]*) extends GuiceModule { - def configure = { - val multibinder = Multibinder.newSetBinder(binder, interface) - implementations.foreach { multibinder.addBinding.to(_).asEagerSingleton } - } -} diff --git a/src/main/scala/com/yammer/dropwizard/modules/GuiceServletModule.scala b/src/main/scala/com/yammer/dropwizard/modules/GuiceServletModule.scala deleted file mode 100644 index 5a6bcf2d522..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/GuiceServletModule.scala +++ /dev/null @@ -1,31 +0,0 @@ -package com.yammer.dropwizard.modules - -import scala.collection.JavaConversions.asJavaMap -import javax.servlet.Filter -import javax.servlet.http.HttpServlet -import com.google.inject.servlet.ServletModule -import com.google.inject.servlet.ServletModule.{ServletKeyBindingBuilder, FilterKeyBindingBuilder} - -/** - * A Scala-friendly wrapper for common Guice servlet bindings. - * - * @author coda - */ -class GuiceServletModule extends ServletModule { - implicit def servletKeyBindingBuilder2scalaBuilder(builder: ServletKeyBindingBuilder) = new { - def using[A <: HttpServlet](implicit mf: Manifest[A]): Unit = - builder.`with`(mf.erasure.asInstanceOf[Class[HttpServlet]]) - - def using[A <: HttpServlet](initParams: Map[String, String])(implicit mf: Manifest[A]): Unit = - builder.`with`(mf.erasure.asInstanceOf[Class[HttpServlet]], asJavaMap(initParams)) - } - - implicit def filterKeyBindingBuilder2scalaBuilder(builder: FilterKeyBindingBuilder) = new { - def through[A <: Filter](implicit mf: Manifest[A]): Unit = - builder.through(mf.erasure.asInstanceOf[Class[Filter]]) - - def through[A <: Filter](initParams: Map[String, String])(implicit mf: Manifest[A]): Unit = - builder.through(mf.erasure.asInstanceOf[Class[Filter]], asJavaMap(initParams)) - } -} - diff --git a/src/main/scala/com/yammer/dropwizard/modules/JerseyServletModule.scala b/src/main/scala/com/yammer/dropwizard/modules/JerseyServletModule.scala deleted file mode 100644 index 0b990539085..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/JerseyServletModule.scala +++ /dev/null @@ -1,11 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.yammer.dropwizard.jersey.ScanningGuiceContainer - -case class JerseyServletModule(rootUri: String) extends GuiceServletModule { - override def configureServlets = { - serve(rootUri).using[ScanningGuiceContainer] - } - - override def toString = "%s(%s)".format(getClass.getCanonicalName, rootUri) -} diff --git a/src/main/scala/com/yammer/dropwizard/modules/ProviderModule.scala b/src/main/scala/com/yammer/dropwizard/modules/ProviderModule.scala deleted file mode 100644 index e155966ee76..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/ProviderModule.scala +++ /dev/null @@ -1,9 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.google.inject.AbstractModule - -abstract class ProviderModule extends AbstractModule { - def configure = {} - - override def toString = getClass.getCanonicalName -} diff --git a/src/main/scala/com/yammer/dropwizard/modules/ServerModule.scala b/src/main/scala/com/yammer/dropwizard/modules/ServerModule.scala deleted file mode 100644 index e6eaa6911f5..00000000000 --- a/src/main/scala/com/yammer/dropwizard/modules/ServerModule.scala +++ /dev/null @@ -1,89 +0,0 @@ -package com.yammer.dropwizard.modules - -import com.codahale.fig.Configuration -import org.eclipse.jetty.util.thread.QueuedThreadPool -import org.eclipse.jetty.server.bio.SocketConnector -import org.eclipse.jetty.server.nio.{SelectChannelConnector, BlockingChannelConnector} -import com.google.inject.{Injector, Singleton, Provides} -import org.eclipse.jetty.server.handler.{HandlerCollection, RequestLogHandler} -import com.yammer.metrics.jetty.InstrumentedHandler -import org.eclipse.jetty.servlet.{DefaultServlet, FilterMapping, ServletContextHandler, ServletHolder} -import com.google.inject.servlet.{GuiceServletContextListener, GuiceFilter} -import org.eclipse.jetty.server.{Connector, Server} -import com.yammer.metrics.reporting.MetricsServlet - -class ServerModule extends ProviderModule { - @Provides - @Singleton - def provideServer(config: Configuration, injector: Injector): Server = { - val server = makeServer(config, mainConnector(config), internalConnector(config)) - - val handlers = new HandlerCollection - handlers.addHandler(new InstrumentedHandler(servletContext(injector))) - handlers.addHandler(internalServletContext) - if (config("request_log.enabled").or(false)) { - handlers.addHandler(injector.getInstance(classOf[RequestLogHandler])) - } - server.setHandler(handlers) - - server - } - - private def newConnector(config: Configuration) = config("http.connector").or("blocking_channel") match { - case "socket" => new SocketConnector - case "select_channel" => new SelectChannelConnector - case "blocking_channel" => new BlockingChannelConnector - } - - private def mainConnector(config: Configuration) = { - val port = config("http.port").or(8080) - val connector = newConnector(config) - config("http.hostname").asOption[String].foreach(connector.setHost) - connector.setForwarded(config("http.forwarded").or(false)) - connector.setPort(port) - connector.setName("main") - connector - } - - private def makeServer(config: Configuration, connectors: Connector*) = { - val server = new Server - connectors.foreach(server.addConnector) - server.setSendServerVersion(false) - server.setThreadPool(makeThreadPool(config)) - server.setStopAtShutdown(true) - server.setGracefulShutdown(config("http.shutdown_milliseconds").or(2000)) - server - } - - private def makeThreadPool(config: Configuration) = { - val pool = new QueuedThreadPool - config("http.max_connections").asOption[Int].foreach(pool.setMaxThreads) - config("http.min_connections").asOption[Int].foreach(pool.setMinThreads) - pool - } - - private def internalConnector(config: Configuration) = { - val connector = newConnector(config) - connector.setPort(config("metrics.port").or(8081)) - connector.setName("internal") - connector - } - - private def servletContext(injector: Injector) = { - val context = new ServletContextHandler() - context.addFilter(classOf[GuiceFilter], "/*", FilterMapping.DEFAULT) - context.addEventListener(new GuiceServletContextListener { - def getInjector = injector - }) - context.addServlet(classOf[DefaultServlet], "/") - context.setConnectorNames(Array("main")) - context - } - - private def internalServletContext = { - val internalContext = new ServletContextHandler() - internalContext.addServlet(new ServletHolder(new MetricsServlet), "/*") - internalContext.setConnectorNames(Array("internal")) - internalContext - } -} diff --git a/src/main/scala/com/yammer/dropwizard/jersey/LoggingExceptionMapper.scala b/src/main/scala/com/yammer/dropwizard/providers/LoggingExceptionMapper.scala similarity index 95% rename from src/main/scala/com/yammer/dropwizard/jersey/LoggingExceptionMapper.scala rename to src/main/scala/com/yammer/dropwizard/providers/LoggingExceptionMapper.scala index 75f48db155a..b53a0d14bda 100644 --- a/src/main/scala/com/yammer/dropwizard/jersey/LoggingExceptionMapper.scala +++ b/src/main/scala/com/yammer/dropwizard/providers/LoggingExceptionMapper.scala @@ -1,4 +1,4 @@ -package com.yammer.dropwizard.jersey +package com.yammer.dropwizard.providers import util.Random import javax.ws.rs.WebApplicationException diff --git a/src/main/scala/com/yammer/dropwizard/service/Jersey.scala b/src/main/scala/com/yammer/dropwizard/service/Jersey.scala deleted file mode 100644 index dba3fb49b91..00000000000 --- a/src/main/scala/com/yammer/dropwizard/service/Jersey.scala +++ /dev/null @@ -1,10 +0,0 @@ -package com.yammer.dropwizard.service - -import com.yammer.dropwizard.modules.JerseyServletModule -import com.yammer.dropwizard.Service - -trait Jersey extends Service { - def rootUri = "/*" - - require(new JerseyServletModule(rootUri)) -} diff --git a/src/test/scala/com/yammer/dropwizard/examples/Example.scala b/src/test/scala/com/yammer/dropwizard/examples/Example.scala index 1c7db93422b..67c32e8398d 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/Example.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/Example.scala @@ -1,9 +1,9 @@ package com.yammer.dropwizard.examples -import com.yammer.dropwizard.Service -import com.yammer.dropwizard.service.Jersey +import com.codahale.fig.Configuration +import com.yammer.dropwizard.{Environment, Service} -object Example extends Service with Jersey { +object Example extends Service { def name = "Example" override def banner = Some(""" @@ -16,9 +16,13 @@ object Example extends Service with Jersey { 88 dP """) - - require(new SayingModule) + provide(new SayCommand, new SplodyCommand) - manage[StartableObject] - healthCheck[DumbHealthCheck] + + def configure(config: Configuration, environment: Environment) { + implicit val template = SayingFactory.buildSaying(config) + environment.addResource(new HelloWorldResource) + environment.addHealthCheck(new DumbHealthCheck) + environment.manage(new StartableObject) + } } diff --git a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala index 6987668e434..35690cd1e41 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala @@ -2,12 +2,10 @@ package com.yammer.dropwizard.examples import javax.ws.rs.{Produces, GET, Path} import javax.ws.rs.core.MediaType -import com.google.inject.{Singleton, Inject} @Path("/hello-world") @Produces(Array(MediaType.APPLICATION_JSON)) -@Singleton -class HelloWorldResource @Inject() (saying: String) { +class HelloWorldResource(implicit saying: String) { @GET def sayHello = Seq(saying) } diff --git a/src/test/scala/com/yammer/dropwizard/examples/SayCommand.scala b/src/test/scala/com/yammer/dropwizard/examples/SayCommand.scala index 16467cd2c6f..80b60ff8703 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/SayCommand.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/SayCommand.scala @@ -1,6 +1,8 @@ package com.yammer.dropwizard.examples import com.yammer.dropwizard.cli.{Flag, ConfiguredCommand} +import com.yammer.dropwizard.Service +import com.codahale.fig.Configuration class SayCommand extends ConfiguredCommand { def name = "say" @@ -8,10 +10,9 @@ class SayCommand extends ConfiguredCommand { override def description = Some("Prints out the saying to console") override def options = Flag("v", "verbose", "yell it a lot") :: Nil - - def runWithConfigFile(opts: Map[String, List[String]], - args: List[String]) = { - val saying = injector.getInstance(classOf[String]) + + def run(service: Service, config: Configuration, opts: Map[String, List[String]], args: List[String]) = { + val saying = SayingFactory.buildSaying(config) for (i <- 1 to (if (opts.contains("verbose")) 10 else 1)) { log.warn(saying) } diff --git a/src/test/scala/com/yammer/dropwizard/examples/SayingFactory.scala b/src/test/scala/com/yammer/dropwizard/examples/SayingFactory.scala new file mode 100644 index 00000000000..d1de90aa5d4 --- /dev/null +++ b/src/test/scala/com/yammer/dropwizard/examples/SayingFactory.scala @@ -0,0 +1,7 @@ +package com.yammer.dropwizard.examples + +import com.codahale.fig.Configuration + +object SayingFactory { + def buildSaying(implicit config: Configuration) = config("saying").asRequired[String] +} diff --git a/src/test/scala/com/yammer/dropwizard/examples/SayingModule.scala b/src/test/scala/com/yammer/dropwizard/examples/SayingModule.scala deleted file mode 100644 index 23c9fa17e44..00000000000 --- a/src/test/scala/com/yammer/dropwizard/examples/SayingModule.scala +++ /dev/null @@ -1,16 +0,0 @@ -package com.yammer.dropwizard.examples - -import com.codahale.fig.Configuration -import com.google.inject.{Singleton, Provides} -import com.yammer.dropwizard.modules.ProviderModule - -class SayingModule extends ProviderModule { - @Provides - @Singleton - def provideSaying(config: Configuration): String = config("saying").asRequired[String] - - @Provides - def shouldNeverBeCalled(confi: Configuration): Int = { - throw new RuntimeException("this should never be called") - } -} diff --git a/src/test/scala/com/yammer/dropwizard/examples/SplodyCommand.scala b/src/test/scala/com/yammer/dropwizard/examples/SplodyCommand.scala index c2bc0822cde..5f818f0c1e2 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/SplodyCommand.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/SplodyCommand.scala @@ -1,6 +1,7 @@ package com.yammer.dropwizard.examples import com.yammer.dropwizard.cli.{Flag, FlagGroup, Command} +import com.yammer.dropwizard.Service class SplodyCommand extends Command { def name = "splody" @@ -9,22 +10,16 @@ class SplodyCommand extends Command { override def options = FlagGroup(Seq(Flag("r", "required", "a required option")),required = true) :: - Flag("g", "guice", "do something dumb with Guice") :: Flag("e", "exception", "throw an exception") :: Flag("m", "message", "return an error message") :: Nil - def run(opts: Map[String, List[String]], args: List[String]) = { - if (opts.contains("guice")) { - println("Using the injector to get an instance of something Guice doesn't know about") - injector.getInstance(classOf[Command]) - } - + def run(service: Service, opts: Map[String, List[String]], args: List[String]) = { if (opts.contains("exception")) { println("Throwing an exception") error("EXPERIENCE BIJ") } - + opts.get("message").map { _ => "Y U NO DO RIGHT THING" } } } diff --git a/src/test/scala/com/yammer/dropwizard/examples/StartableObject.scala b/src/test/scala/com/yammer/dropwizard/examples/StartableObject.scala index 5d54f162e29..51820d8f483 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/StartableObject.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/StartableObject.scala @@ -1,10 +1,9 @@ package com.yammer.dropwizard.examples import com.codahale.logula.Logging -import com.google.inject.Inject import com.yammer.dropwizard.lifecycle.Managed -class StartableObject @Inject()(template: String) extends Managed with Logging { +class StartableObject(implicit template: String) extends Managed with Logging { override def start() { log.info("Starting: %s", template) } From a2ca27f78963a7ae70f408b850bb837ea4f8af2c Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 01:01:36 -0700 Subject: [PATCH 03/25] Make configure params implicit. --- src/main/scala/com/yammer/dropwizard/Service.scala | 2 +- src/test/scala/com/yammer/dropwizard/examples/Example.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Service.scala b/src/main/scala/com/yammer/dropwizard/Service.scala index 514af3d8d5d..8ef776aa74d 100644 --- a/src/main/scala/com/yammer/dropwizard/Service.scala +++ b/src/main/scala/com/yammer/dropwizard/Service.scala @@ -15,7 +15,7 @@ trait Service extends Logging with JarAware { def banner: Option[String] = None - def configure(config: Configuration, environment: Environment) + def configure(implicit config: Configuration, environment: Environment) private def printUsage(error: Option[String] = None) { for (msg <- error) { diff --git a/src/test/scala/com/yammer/dropwizard/examples/Example.scala b/src/test/scala/com/yammer/dropwizard/examples/Example.scala index 67c32e8398d..9c99ea9e7e2 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/Example.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/Example.scala @@ -19,8 +19,8 @@ object Example extends Service { provide(new SayCommand, new SplodyCommand) - def configure(config: Configuration, environment: Environment) { - implicit val template = SayingFactory.buildSaying(config) + def configure(implicit config: Configuration, environment: Environment) { + implicit val template = SayingFactory.buildSaying environment.addResource(new HelloWorldResource) environment.addHealthCheck(new DumbHealthCheck) environment.manage(new StartableObject) From 2e7737f5efc371b73d9471bd0ca9836a81c516a6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 01:10:13 -0700 Subject: [PATCH 04/25] Remove unused import. --- src/main/scala/com/yammer/dropwizard/Environment.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 08a4f434063..ef6781a9e81 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -5,7 +5,7 @@ import com.sun.jersey.api.core.ResourceConfig._ import com.codahale.logula.Logging import com.sun.jersey.api.core.DefaultResourceConfig import com.yammer.metrics.HealthChecks -import com.yammer.metrics.core.{DeadlockHealthCheck, HealthCheck} +import com.yammer.metrics.core.HealthCheck class Environment extends DefaultResourceConfig with Logging { private[dropwizard] var resources = Set.empty[Object] From 678b025a11686fc39cc5c304d4d1d43d2c16768b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 07:24:21 -0700 Subject: [PATCH 05/25] Distinguish this branch's builds. --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 484ef2fbafd..0a7a2808354 100644 --- a/project/build.properties +++ b/project/build.properties @@ -3,6 +3,6 @@ project.organization=com.yammer project.name=dropwizard sbt.version=0.7.5.RC0 -project.version=0.0.4-SNAPSHOT +project.version=0.0.4-OVERHAUL-SNAPSHOT build.scala.versions=2.8.1 project.initialize=false From 11e93d38ac4673c94635867a7666268775a79d26 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 10:22:54 -0700 Subject: [PATCH 06/25] Add default providers and deadlock health check back in. --- src/main/scala/com/yammer/dropwizard/Environment.scala | 10 +++++++++- .../com/yammer/dropwizard/cli/ServerCommand.scala | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index ef6781a9e81..4d68c6b094f 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -6,11 +6,19 @@ import com.codahale.logula.Logging import com.sun.jersey.api.core.DefaultResourceConfig import com.yammer.metrics.HealthChecks import com.yammer.metrics.core.HealthCheck +import providers.LoggingExceptionMapper +import com.codahale.jersey.inject.ScalaCollectionsQueryParamInjectableProvider +import com.codahale.jersey.providers.{JValueProvider, JsonCaseClassProvider} class Environment extends DefaultResourceConfig with Logging { private[dropwizard] var resources = Set.empty[Object] private[dropwizard] var healthChecks = Set.empty[HealthCheck] - private[dropwizard] var providers = Set.empty[Object] + private[dropwizard] var providers = Set[Object]( + new LoggingExceptionMapper, + new JsonCaseClassProvider, + new ScalaCollectionsQueryParamInjectableProvider, + new JValueProvider + ) private[dropwizard] var managedObjects = IndexedSeq.empty[Managed] def addResource(resource: Object) { diff --git a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala index 62fd986302a..51671677a46 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala @@ -3,9 +3,9 @@ package com.yammer.dropwizard.cli import com.yammer.metrics.HealthChecks import com.codahale.fig.Configuration import com.yammer.dropwizard.config.ServerFactory -import com.yammer.metrics.core.DeadlockHealthCheck import com.sun.jersey.spi.container.servlet.ServletContainer import com.yammer.dropwizard.{Environment, Service} +import com.yammer.metrics.core.DeadlockHealthCheck import com.yammer.dropwizard.lifecycle.JettyManager class ServerCommand(service: Service) extends ConfiguredCommand { @@ -21,6 +21,7 @@ class ServerCommand(service: Service) extends ConfiguredCommand { val env = new Environment service.configure(config, env) env.healthChecks.foreach(HealthChecks.register) + HealthChecks.register(new DeadlockHealthCheck) val servlet = new ServletContainer(env) val server = ServerFactory.provideServer(config, servlet) From ae997009760f964a0a5367b19ff3748bcd363c5b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Apr 2011 10:29:12 -0700 Subject: [PATCH 07/25] No really, fix the providers. --- src/main/scala/com/yammer/dropwizard/Environment.scala | 9 +++++---- .../yammer/dropwizard/examples/HelloWorldResource.scala | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 4d68c6b094f..cd7d0719b4b 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -11,14 +11,15 @@ import com.codahale.jersey.inject.ScalaCollectionsQueryParamInjectableProvider import com.codahale.jersey.providers.{JValueProvider, JsonCaseClassProvider} class Environment extends DefaultResourceConfig with Logging { - private[dropwizard] var resources = Set.empty[Object] - private[dropwizard] var healthChecks = Set.empty[HealthCheck] - private[dropwizard] var providers = Set[Object]( + Seq( new LoggingExceptionMapper, new JsonCaseClassProvider, new ScalaCollectionsQueryParamInjectableProvider, new JValueProvider - ) + ).foreach(getSingletons.add) + private[dropwizard] var resources = Set.empty[Object] + private[dropwizard] var healthChecks = Set.empty[HealthCheck] + private[dropwizard] var providers = Set.empty[Object] private[dropwizard] var managedObjects = IndexedSeq.empty[Managed] def addResource(resource: Object) { diff --git a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala index 35690cd1e41..70a5a3ceea7 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala @@ -1,11 +1,11 @@ package com.yammer.dropwizard.examples -import javax.ws.rs.{Produces, GET, Path} import javax.ws.rs.core.MediaType +import javax.ws.rs.{QueryParam, Produces, GET, Path} @Path("/hello-world") @Produces(Array(MediaType.APPLICATION_JSON)) class HelloWorldResource(implicit saying: String) { @GET - def sayHello = Seq(saying) + def sayHello(@QueryParam("opt") opt: Option[String]) = Seq(saying) } From 434e8082bdf7b6ee39242b038ddaa8a9c7860872 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 1 May 2011 14:05:06 -0700 Subject: [PATCH 08/25] Depend on Guiceless Metrics. --- project/build/DropwizardProject.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project/build/DropwizardProject.scala b/project/build/DropwizardProject.scala index 74d264dcb88..8431f35eca8 100644 --- a/project/build/DropwizardProject.scala +++ b/project/build/DropwizardProject.scala @@ -41,7 +41,9 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) */ val fig = "com.codahale" %% "fig" % "1.1.1" val jerkson = "com.codahale" %% "jerkson" % "0.1.7" - val metrics = "com.yammer" %% "metrics" % "2.0.0-BETA11" + val metricsCore = "com.yammer.metrics" %% "metrics-core" % "2.0.0-BETA12-SNAPSHOT" + val metricsServlet = "com.yammer.metrics" %% "metrics-servlet" % "2.0.0-BETA12-SNAPSHOT" + val metricsJetty = "com.yammer.metrics" %% "metrics-jetty" % "2.0.0-BETA12-SNAPSHOT" val commonsCli = "commons-cli" % "commons-cli" % "1.2" val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % "1.7.5" val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.7.5" From 35b4967723f1c1b6398c15b2ac8faa13fee4ad28 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 1 May 2011 19:18:31 -0700 Subject: [PATCH 09/25] Added logging instrumentation. --- project/build/DropwizardProject.scala | 8 +++++--- .../yammer/dropwizard/config/ConfigurationFactory.scala | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/project/build/DropwizardProject.scala b/project/build/DropwizardProject.scala index 8431f35eca8..c58a1e174a7 100644 --- a/project/build/DropwizardProject.scala +++ b/project/build/DropwizardProject.scala @@ -41,9 +41,11 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) */ val fig = "com.codahale" %% "fig" % "1.1.1" val jerkson = "com.codahale" %% "jerkson" % "0.1.7" - val metricsCore = "com.yammer.metrics" %% "metrics-core" % "2.0.0-BETA12-SNAPSHOT" - val metricsServlet = "com.yammer.metrics" %% "metrics-servlet" % "2.0.0-BETA12-SNAPSHOT" - val metricsJetty = "com.yammer.metrics" %% "metrics-jetty" % "2.0.0-BETA12-SNAPSHOT" + val metricsVersion = "2.0.0-BETA12-SNAPSHOT" + val metricsCore = "com.yammer.metrics" %% "metrics-core" % metricsVersion + val metricsServlet = "com.yammer.metrics" %% "metrics-servlet" % metricsVersion + val metricsJetty = "com.yammer.metrics" %% "metrics-jetty" % metricsVersion + val metricsLog4j = "com.yammer.metrics" %% "metrics-log4j" % metricsVersion val commonsCli = "commons-cli" % "commons-cli" % "1.2" val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % "1.7.5" val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.7.5" diff --git a/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala index b9b196b0ed9..847f7d20692 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ConfigurationFactory.scala @@ -4,7 +4,9 @@ import com.codahale.fig.Configuration import java.util.logging.Logger import org.slf4j.bridge.SLF4JBridgeHandler import com.codahale.logula.Logging -import org.apache.log4j.Level +import org.apache.log4j.{LogManager, Level} +import org.apache.log4j.varia.NullAppender +import com.yammer.metrics.log4j.InstrumentedAppender object ConfigurationFactory { def buildConfiguration(filename: String) = { @@ -48,5 +50,8 @@ object ConfigurationFactory { log.syslog.facility = config("logging.syslog.facility").asRequired[String] } } + + // add in an instrumented null appender to get full logging stats + LogManager.getRootLogger.addAppender(new InstrumentedAppender(new NullAppender)) } } From 85bfed36e630e73b7e19de7380c2a51e47bea61d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 1 May 2011 23:45:20 -0700 Subject: [PATCH 10/25] Not really using Guice for this. --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1e4373f4a94..a114d26fdf5 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ Dropwizard It's a little bit of opinionated glue code which bangs together a set of libraries which have historically not sucked: -- [Jetty](http://www.eclipse.org/jetty/) for HTTP servin'. -- [Jersey](http://jersey.java.net/) for REST modelin'. -- [Guice](http://code.google.com/p/google-guice/) for dependency injectin'. -- [Fig](https://github.com/codahale/fig) for JSON configurin'. -- [Jerkson](https://github.com/codahale/jerkson)/[Jackson](http://jackson.codehaus.org) for JSON parsin' and generatin'. -- [Logula](https://github.com/codahale/logula)/[Log4j](http://logging.apache.org/log4j/1.2/) for loggin'. -- [Metrics](https://github.com/codahale/metrics) for figurin' out what your service is doing in production. +* [Jetty](http://www.eclipse.org/jetty/) for HTTP servin'. +* [Jersey](http://jersey.java.net/) for REST modelin'. +* [Fig](https://github.com/codahale/fig) for JSON configurin'. +* [Jerkson](https://github.com/codahale/jerkson)/[Jackson](http://jackson.codehaus.org) for JSON parsin' and generatin'. +* [Logula](https://github.com/codahale/logula)/[Log4j](http://logging.apache.org/log4j/1.2/) for loggin'. +* [Metrics](https://github.com/codahale/metrics) for figurin' out what your service is doing in production. [Yammer](https://www.yammer.com)'s high-performance, low-latency, Scala services all use Dropwizard. In fact, Dropwizard is really just a simple extraction of [Yammer](https://www.yammer.com)'s glue code. From f25cf8bb0a08c05af17459e48b287de467bba9d5 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 09:48:31 -0700 Subject: [PATCH 11/25] Add support for arbitrary servlets and filters. --- .../com/yammer/dropwizard/Environment.scala | 29 ++++++++----------- .../com/yammer/dropwizard/JerseyConfig.scala | 22 ++++++++++++++ .../yammer/dropwizard/cli/ServerCommand.scala | 6 ++-- .../dropwizard/config/ServerFactory.scala | 19 ++++++++---- 4 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 src/main/scala/com/yammer/dropwizard/JerseyConfig.scala diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index cd7d0719b4b..857ec27b96c 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -3,24 +3,16 @@ package com.yammer.dropwizard import lifecycle.Managed import com.sun.jersey.api.core.ResourceConfig._ import com.codahale.logula.Logging -import com.sun.jersey.api.core.DefaultResourceConfig -import com.yammer.metrics.HealthChecks import com.yammer.metrics.core.HealthCheck -import providers.LoggingExceptionMapper -import com.codahale.jersey.inject.ScalaCollectionsQueryParamInjectableProvider -import com.codahale.jersey.providers.{JValueProvider, JsonCaseClassProvider} +import javax.servlet.{Servlet, Filter} -class Environment extends DefaultResourceConfig with Logging { - Seq( - new LoggingExceptionMapper, - new JsonCaseClassProvider, - new ScalaCollectionsQueryParamInjectableProvider, - new JValueProvider - ).foreach(getSingletons.add) +class Environment extends Logging { private[dropwizard] var resources = Set.empty[Object] private[dropwizard] var healthChecks = Set.empty[HealthCheck] private[dropwizard] var providers = Set.empty[Object] private[dropwizard] var managedObjects = IndexedSeq.empty[Managed] + private[dropwizard] var filters = Map.empty[String, Filter] + private[dropwizard] var servlets = Map.empty[String, Servlet] def addResource(resource: Object) { if (!isRootResourceClass(resource.getClass)) { @@ -28,7 +20,6 @@ class Environment extends DefaultResourceConfig with Logging { " is not a @Path-annotated resource class") } resources += resource - getSingletons.add(resource) } def addProvider(provider: Object) { @@ -37,21 +28,25 @@ class Environment extends DefaultResourceConfig with Logging { " is not a @Provider-annotated provider class") } providers += provider - getSingletons.add(provider) } def addHealthCheck(healthCheck: HealthCheck) { healthChecks += healthCheck - HealthChecks.register(healthCheck) } def manage(managedObject: Managed) { managedObjects ++= IndexedSeq(managedObject) } - override def validate() { - super.validate() + def addFilter(filter: Filter, pathSpec: String) { + filters += pathSpec -> filter + } + + def addServlet(servlet: Servlet, pathSpec: String) { + servlets += pathSpec -> servlet + } + private[dropwizard] def validate() { log.info("resources = %s", resources.mkString("{", ", ", "}")) log.info("providers = %s", providers.mkString("{", ", ", "}")) log.info("health checks = %s", healthChecks.mkString("{", ", ", "}")) diff --git a/src/main/scala/com/yammer/dropwizard/JerseyConfig.scala b/src/main/scala/com/yammer/dropwizard/JerseyConfig.scala new file mode 100644 index 00000000000..efb1c55bf80 --- /dev/null +++ b/src/main/scala/com/yammer/dropwizard/JerseyConfig.scala @@ -0,0 +1,22 @@ +package com.yammer.dropwizard + +import com.sun.jersey.api.core.DefaultResourceConfig +import com.codahale.logula.Logging +import providers.LoggingExceptionMapper +import com.codahale.jersey.inject.ScalaCollectionsQueryParamInjectableProvider +import com.codahale.jersey.providers.{JValueProvider, JsonCaseClassProvider} + +class JerseyConfig(env: Environment) extends DefaultResourceConfig with Logging { + ( + Set( + new LoggingExceptionMapper, + new JsonCaseClassProvider, + new ScalaCollectionsQueryParamInjectableProvider, + new JValueProvider + ) ++ env.resources ++ env.providers + ).foreach(getSingletons.add) + + override def validate() { + env.validate() + } +} diff --git a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala index 51671677a46..bdafcc97c11 100644 --- a/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala +++ b/src/main/scala/com/yammer/dropwizard/cli/ServerCommand.scala @@ -4,9 +4,9 @@ import com.yammer.metrics.HealthChecks import com.codahale.fig.Configuration import com.yammer.dropwizard.config.ServerFactory import com.sun.jersey.spi.container.servlet.ServletContainer -import com.yammer.dropwizard.{Environment, Service} import com.yammer.metrics.core.DeadlockHealthCheck import com.yammer.dropwizard.lifecycle.JettyManager +import com.yammer.dropwizard.{JerseyConfig, Environment, Service} class ServerCommand(service: Service) extends ConfiguredCommand { def name = "server" @@ -22,9 +22,9 @@ class ServerCommand(service: Service) extends ConfiguredCommand { service.configure(config, env) env.healthChecks.foreach(HealthChecks.register) HealthChecks.register(new DeadlockHealthCheck) + env.addServlet(new ServletContainer(new JerseyConfig(env)), "/*") - val servlet = new ServletContainer(env) - val server = ServerFactory.provideServer(config, servlet) + val server = ServerFactory.provideServer(config, env.servlets, env.filters) env.managedObjects.map { new JettyManager(_) }.foreach(server.addBean) log.info("Starting %s", service.name) diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala index 7cfd4a57bfd..da004d10c04 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -5,14 +5,15 @@ import com.yammer.metrics.jetty.InstrumentedHandler import org.eclipse.jetty.server.handler.HandlerCollection import org.eclipse.jetty.server.bio.SocketConnector import org.eclipse.jetty.server.nio.{BlockingChannelConnector, SelectChannelConnector} -import org.eclipse.jetty.server.{Server, Connector} import org.eclipse.jetty.util.thread.QueuedThreadPool import com.yammer.metrics.reporting.MetricsServlet -import javax.servlet.Servlet import org.eclipse.jetty.servlet._ +import javax.servlet.{Filter, Servlet} +import java.util.EnumSet +import org.eclipse.jetty.server.{DispatcherType, Server, Connector} object ServerFactory { - def provideServer(implicit config: Configuration, servlet: Servlet) = { + def provideServer(implicit config: Configuration, servlets: Map[String, Servlet], filters: Map[String, Filter]) = { val server = makeServer(mainConnector, internalConnector) val handlers = new HandlerCollection @@ -67,9 +68,17 @@ object ServerFactory { connector } - private def servletContext(implicit servlet: Servlet) = { + private def servletContext(implicit servlets: Map[String, Servlet], filters: Map[String, Filter]) = { val context = new ServletContextHandler() - context.addServlet(new ServletHolder(servlet), "/") + + for ((pathSpec, servlet) <- servlets) { + context.addServlet(new ServletHolder(servlet), pathSpec) + } + + for ((pathSpec, filter) <- filters) { + context.addFilter(new FilterHolder(filter), pathSpec, EnumSet.of(DispatcherType.REQUEST)) + } + context.setConnectorNames(Array("main")) context } From 6d4a572482c6e442459b22fde3e81c797b988b1e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 09:48:47 -0700 Subject: [PATCH 12/25] Convince, don't threaten. --- src/main/scala/com/yammer/dropwizard/Environment.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 857ec27b96c..500a5b6fcdc 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -59,7 +59,9 @@ class Environment extends Logging { !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! THIS SERVICE HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW IF IT ! ! DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE LETTING ! -! YOUR USERS DOWN. ADD HEALTHCHECKS OR FEAR THE WRATH OF THE DROP WIZARD. ! +! YOUR USERS DOWN. YOU SHOULD ADD A HEALTHCHECK FOR EACH DEPENDENCY OF ! +! YOUR SERVICE WHICH FULLY (BUT LIGHTLY) TESTS YOUR SERVICE'S ABILITY TO ! +! USE THAT SERVICE. THINK OF IT AS A CONTINUOUS INTEGRATION TEST. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! """) From 9dc0994be04eb6b82519f241205ae61af7036181 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 11:36:34 -0700 Subject: [PATCH 13/25] Check resources for annotated methods. Fail early. --- src/main/scala/com/yammer/dropwizard/Environment.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 500a5b6fcdc..20d61f80b55 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -1,10 +1,13 @@ package com.yammer.dropwizard +import collection.JavaConversions._ import lifecycle.Managed import com.sun.jersey.api.core.ResourceConfig._ import com.codahale.logula.Logging import com.yammer.metrics.core.HealthCheck import javax.servlet.{Servlet, Filter} +import com.sun.jersey.core.reflection.MethodList +import javax.ws.rs.HttpMethod class Environment extends Logging { private[dropwizard] var resources = Set.empty[Object] @@ -19,6 +22,12 @@ class Environment extends Logging { throw new IllegalArgumentException(resource.getClass.getCanonicalName + " is not a @Path-annotated resource class") } + + if (new MethodList(resource.getClass, true).hasMetaAnnotation(classOf[HttpMethod]).isEmpty) { + throw new IllegalArgumentException(resource.getClass.getCanonicalName + + " has no @GET/@POST/etc-annotated methods") + } + resources += resource } From e5146a96de882a16305969f58a5cf9819e37ce52 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 11:54:29 -0700 Subject: [PATCH 14/25] Log all available resources and their methods. --- .../com/yammer/dropwizard/Environment.scala | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 20d61f80b55..a2887c57b63 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -7,7 +7,7 @@ import com.codahale.logula.Logging import com.yammer.metrics.core.HealthCheck import javax.servlet.{Servlet, Filter} import com.sun.jersey.core.reflection.MethodList -import javax.ws.rs.HttpMethod +import javax.ws.rs.{Path, HttpMethod} class Environment extends Logging { private[dropwizard] var resources = Set.empty[Object] @@ -23,7 +23,7 @@ class Environment extends Logging { " is not a @Path-annotated resource class") } - if (new MethodList(resource.getClass, true).hasMetaAnnotation(classOf[HttpMethod]).isEmpty) { + if (annotatedMethods(resource).isEmpty) { throw new IllegalArgumentException(resource.getClass.getCanonicalName + " has no @GET/@POST/etc-annotated methods") } @@ -56,10 +56,29 @@ class Environment extends Logging { } private[dropwizard] def validate() { - log.info("resources = %s", resources.mkString("{", ", ", "}")) - log.info("providers = %s", providers.mkString("{", ", ", "}")) - log.info("health checks = %s", healthChecks.mkString("{", ", ", "}")) - log.info("managed objects = %s", managedObjects.mkString("{", ", ", "}")) + def logResources() { + def httpMethods(resource: Object) = annotatedMethods(resource).map { + _.getMetaMethodAnnotations(classOf[HttpMethod]).map { _.value() } + }.flatten.toIndexedSeq.sorted + + def paths(resource: Object) = + resource.getClass.getAnnotation(classOf[Path]).value() :: Nil + + val out = new StringBuilder("\n\n") + for (resource <- resources; + path <- paths(resource); + method <- httpMethods(resource)) { + out.append(" %s %s (%s)\n".format(method, path, resource.getClass.getCanonicalName)) + } + log.info(out.toString) + } + + log.debug("resources = %s", resources.mkString("{", ", ", "}")) + log.debug("providers = %s", providers.mkString("{", ", ", "}")) + log.debug("health checks = %s", healthChecks.mkString("{", ", ", "}")) + log.debug("managed objects = %s", managedObjects.mkString("{", ", ", "}")) + + logResources() if (healthChecks.isEmpty) { log.warn(""" @@ -76,4 +95,7 @@ class Environment extends Logging { """) } } + + private def annotatedMethods(resource: Object) = + new MethodList(resource.getClass, true).hasMetaAnnotation(classOf[HttpMethod]) } From f1239565cce3a821575e048fb2de0bdb171d2405 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 20:17:45 -0700 Subject: [PATCH 15/25] No longer overhauling. Just haulin'. Ass, that is. --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 0a7a2808354..484ef2fbafd 100644 --- a/project/build.properties +++ b/project/build.properties @@ -3,6 +3,6 @@ project.organization=com.yammer project.name=dropwizard sbt.version=0.7.5.RC0 -project.version=0.0.4-OVERHAUL-SNAPSHOT +project.version=0.0.4-SNAPSHOT build.scala.versions=2.8.1 project.initialize=false From 32a4e173595a47dd49daad4cc0cb1f13e2f325e0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 23:20:08 -0700 Subject: [PATCH 16/25] Add the ability to set up traditional (read: broken) servlets and filters. --- .../com/yammer/dropwizard/Environment.scala | 29 +++++++++++++++---- .../dropwizard/config/ServerFactory.scala | 11 ++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index a2887c57b63..c082771799f 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -8,14 +8,15 @@ import com.yammer.metrics.core.HealthCheck import javax.servlet.{Servlet, Filter} import com.sun.jersey.core.reflection.MethodList import javax.ws.rs.{Path, HttpMethod} +import org.eclipse.jetty.servlet.{ServletHolder, FilterHolder} class Environment extends Logging { private[dropwizard] var resources = Set.empty[Object] private[dropwizard] var healthChecks = Set.empty[HealthCheck] private[dropwizard] var providers = Set.empty[Object] private[dropwizard] var managedObjects = IndexedSeq.empty[Managed] - private[dropwizard] var filters = Map.empty[String, Filter] - private[dropwizard] var servlets = Map.empty[String, Servlet] + private[dropwizard] var filters = Map.empty[String, FilterHolder] + private[dropwizard] var servlets = Map.empty[String, ServletHolder] def addResource(resource: Object) { if (!isRootResourceClass(resource.getClass)) { @@ -48,11 +49,29 @@ class Environment extends Logging { } def addFilter(filter: Filter, pathSpec: String) { - filters += pathSpec -> filter + filters += pathSpec -> new FilterHolder(filter) + } + + def addFilter(klass: Class[_ <: Filter], + pathSpec: String, + params: Map[String, String] = Map.empty) { + val holder = new FilterHolder(klass) + holder.setInitParameters(params) + filters += pathSpec -> holder } def addServlet(servlet: Servlet, pathSpec: String) { - servlets += pathSpec -> servlet + servlets += pathSpec -> new ServletHolder(servlet) + } + + def addServlet(klass: Class[_ <: Servlet], + pathSpec: String, + params: Map[String, String] = Map.empty, + initOrder: Int = 0) { + val holder = new ServletHolder(klass) + holder.setInitParameters(params) + holder.setInitOrder(initOrder) + servlets += pathSpec -> holder } private[dropwizard] def validate() { @@ -70,7 +89,7 @@ class Environment extends Logging { method <- httpMethods(resource)) { out.append(" %s %s (%s)\n".format(method, path, resource.getClass.getCanonicalName)) } - log.info(out.toString) + log.info(out.toString()) } log.debug("resources = %s", resources.mkString("{", ", ", "}")) diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala index da004d10c04..898ec5fe584 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -13,7 +13,9 @@ import java.util.EnumSet import org.eclipse.jetty.server.{DispatcherType, Server, Connector} object ServerFactory { - def provideServer(implicit config: Configuration, servlets: Map[String, Servlet], filters: Map[String, Filter]) = { + def provideServer(implicit config: Configuration, + servlets: Map[String, ServletHolder], + filters: Map[String, FilterHolder]) = { val server = makeServer(mainConnector, internalConnector) val handlers = new HandlerCollection @@ -68,15 +70,16 @@ object ServerFactory { connector } - private def servletContext(implicit servlets: Map[String, Servlet], filters: Map[String, Filter]) = { + private def servletContext(implicit servlets: Map[String, ServletHolder], + filters: Map[String, FilterHolder]) = { val context = new ServletContextHandler() for ((pathSpec, servlet) <- servlets) { - context.addServlet(new ServletHolder(servlet), pathSpec) + context.addServlet(servlet, pathSpec) } for ((pathSpec, filter) <- filters) { - context.addFilter(new FilterHolder(filter), pathSpec, EnumSet.of(DispatcherType.REQUEST)) + context.addFilter(filter, pathSpec, EnumSet.of(DispatcherType.REQUEST)) } context.setConnectorNames(Array("main")) From 08dcdd7e58a75a451321b34c4133190cb82d9d7b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 23:26:52 -0700 Subject: [PATCH 17/25] Even more servlet/filter funk. --- .../com/yammer/dropwizard/Environment.scala | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index c082771799f..746cd3497eb 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -48,8 +48,12 @@ class Environment extends Logging { managedObjects ++= IndexedSeq(managedObject) } - def addFilter(filter: Filter, pathSpec: String) { - filters += pathSpec -> new FilterHolder(filter) + def addFilter(filter: Filter, + pathSpec: String, + params: Map[String, String] = Map.empty) { + val holder = new FilterHolder(filter) + holder.setInitParameters(params) + filters += pathSpec -> holder } def addFilter(klass: Class[_ <: Filter], @@ -60,8 +64,14 @@ class Environment extends Logging { filters += pathSpec -> holder } - def addServlet(servlet: Servlet, pathSpec: String) { - servlets += pathSpec -> new ServletHolder(servlet) + def addServlet(servlet: Servlet, + pathSpec: String, + params: Map[String, String] = Map.empty, + initOrder: Int = 0) { + val holder = new ServletHolder(servlet) + holder.setInitParameters(params) + holder.setInitOrder(initOrder) + servlets += pathSpec -> holder } def addServlet(klass: Class[_ <: Servlet], From eb9977aa7706de91ad75dc0c2a518b534aaa312a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 2 May 2011 23:32:01 -0700 Subject: [PATCH 18/25] Method overloading and default arguments don't agree. --- .../com/yammer/dropwizard/Environment.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/Environment.scala b/src/main/scala/com/yammer/dropwizard/Environment.scala index 746cd3497eb..d1875ded1d8 100644 --- a/src/main/scala/com/yammer/dropwizard/Environment.scala +++ b/src/main/scala/com/yammer/dropwizard/Environment.scala @@ -48,36 +48,36 @@ class Environment extends Logging { managedObjects ++= IndexedSeq(managedObject) } - def addFilter(filter: Filter, - pathSpec: String, - params: Map[String, String] = Map.empty) { + def addFilter[T <: Filter](filter: T, + pathSpec: String, + params: Map[String, String] = Map.empty) { val holder = new FilterHolder(filter) holder.setInitParameters(params) filters += pathSpec -> holder } - def addFilter(klass: Class[_ <: Filter], - pathSpec: String, - params: Map[String, String] = Map.empty) { + def addFilterClass[T <: Filter](klass: Class[T], + pathSpec: String, + params: Map[String, String] = Map.empty) { val holder = new FilterHolder(klass) holder.setInitParameters(params) filters += pathSpec -> holder } - def addServlet(servlet: Servlet, - pathSpec: String, - params: Map[String, String] = Map.empty, - initOrder: Int = 0) { + def addServlet[T <: Servlet](servlet: T, + pathSpec: String, + params: Map[String, String] = Map.empty, + initOrder: Int = 0) { val holder = new ServletHolder(servlet) holder.setInitParameters(params) holder.setInitOrder(initOrder) servlets += pathSpec -> holder } - def addServlet(klass: Class[_ <: Servlet], - pathSpec: String, - params: Map[String, String] = Map.empty, - initOrder: Int = 0) { + def addServletClass[T <: Servlet](klass: Class[T], + pathSpec: String, + params: Map[String, String] = Map.empty, + initOrder: Int = 0) { val holder = new ServletHolder(klass) holder.setInitParameters(params) holder.setInitOrder(initOrder) From ce6cf94e395bc2f0cd07d3bfce0948bb20fb39f3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 3 May 2011 00:37:37 -0700 Subject: [PATCH 19/25] Add more specific SelectChannelConnector config. --- .../dropwizard/config/ServerFactory.scala | 13 ++++++-- src/test/resources/example.conf | 30 +++++++++++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala index 898ec5fe584..1a01cdad96b 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -32,7 +32,14 @@ object ServerFactory { private def newConnector(implicit config: Configuration) = config("http.connector").or("blocking_channel") match { case "socket" => new SocketConnector - case "select_channel" => new SelectChannelConnector + case "select_channel" => { + val connector = new SelectChannelConnector + connector.setAcceptors(config("http.acceptor_threads").or(2)) + connector.setMaxIdleTime(config("http.max_idle_time_seconds").or(300)) + connector.setLowResourcesConnections(config("http.low_resources_connections").or(25000)) + connector.setLowResourcesMaxIdleTime(config("http.low_resources_max_idle_time_seconds").or(5)) + connector + } case "blocking_channel" => new BlockingChannelConnector } @@ -58,8 +65,8 @@ object ServerFactory { private def makeThreadPool(implicit config: Configuration) = { val pool = new QueuedThreadPool - config("http.max_connections").asOption[Int].foreach(pool.setMaxThreads) - config("http.min_connections").asOption[Int].foreach(pool.setMinThreads) + config("http.max_threads").asOption[Int].foreach(pool.setMaxThreads) + config("http.min_threads").asOption[Int].foreach(pool.setMinThreads) pool } diff --git a/src/test/resources/example.conf b/src/test/resources/example.conf index 0df501cf523..92a550e3197 100644 --- a/src/test/resources/example.conf +++ b/src/test/resources/example.conf @@ -6,14 +6,34 @@ // "socket". "connector": "blocking_channel", + // The maximum idle time for a connection. Only valid for select_channel + // connector. + //"max_idle_time_seconds": 300, + + // The number of acceptor threads to set. Only valid for select_channel + // connector. + //"acceptor_threads": 2, + + // Set the number of connections, which if exceeded places the manager in + // low resources state. This is not an exact measure as the connection count + // is averaged over the select sets. Only valid for select_channel + // connector. + //"low_resources_connections": 25000, + + // The amount of time that a connection is allowed to be idle when there are + // more than `low_resources_connections` connections. This allows the server + // to rapidly close idle connections in order to gracefully handle high load + // situations. Only valid for select_channel connector. + //"low_resources_max_idle_time_seconds": 5 + // The TCP/IP port Jetty will host your server on. "port": 8080, - // Maximum number of concurrent connections. - "max_connections": 50, + // Maximum number of threads. + "max_threads": 50, // Minimum number of thread to keep alive. - "min_connections": 10, + "min_threads": 10, // Number of milliseconds to wait for connections to complete while // gracefully shutting down. @@ -54,7 +74,7 @@ "enabled": false, // The filename pattern for the log file. - "filename": "./logs/notifications.log", + "filename": "./logs/example.log", // Let logs get to 50MB in size. "max_log_size_kilobytes": 51200, @@ -90,7 +110,7 @@ "extended": false, // URIs which will not be logged. - "ignore_paths": ["/health"], + "ignore_paths": [], // Whether to log cookie values. "include_cookies": false, From 802026a7f0a40644fa4901798396381903d94d50 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 3 May 2011 12:50:25 -0700 Subject: [PATCH 20/25] Fix example config syntax. --- src/test/resources/example.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/example.conf b/src/test/resources/example.conf index 92a550e3197..a09dfbcf62a 100644 --- a/src/test/resources/example.conf +++ b/src/test/resources/example.conf @@ -24,7 +24,7 @@ // more than `low_resources_connections` connections. This allows the server // to rapidly close idle connections in order to gracefully handle high load // situations. Only valid for select_channel connector. - //"low_resources_max_idle_time_seconds": 5 + //"low_resources_max_idle_time_seconds": 5, // The TCP/IP port Jetty will host your server on. "port": 8080, From 2c680da544cbbbff1ec4e3557d6d669efe3b38a8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 3 May 2011 12:50:45 -0700 Subject: [PATCH 21/25] Just use a SocketConnector for the internal port. --- src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala index 1a01cdad96b..5aed4fc323d 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -71,7 +71,7 @@ object ServerFactory { } private def internalConnector(implicit config: Configuration) = { - val connector = newConnector(config) + val connector = new SocketConnector connector.setPort(config("metrics.port").or(8081)) connector.setName("internal") connector From 39985758e0bd43283f566b1461224f52ecc32e36 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 5 May 2011 18:36:59 -0700 Subject: [PATCH 22/25] Bump to Jerkson 0.1.8 and Jackson 1.7.6. --- project/build/DropwizardProject.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/build/DropwizardProject.scala b/project/build/DropwizardProject.scala index c58a1e174a7..6fffb9494b8 100644 --- a/project/build/DropwizardProject.scala +++ b/project/build/DropwizardProject.scala @@ -40,15 +40,15 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) * Misc Dependencies */ val fig = "com.codahale" %% "fig" % "1.1.1" - val jerkson = "com.codahale" %% "jerkson" % "0.1.7" + val jerkson = "com.codahale" %% "jerkson" % "0.1.8" val metricsVersion = "2.0.0-BETA12-SNAPSHOT" val metricsCore = "com.yammer.metrics" %% "metrics-core" % metricsVersion val metricsServlet = "com.yammer.metrics" %% "metrics-servlet" % metricsVersion val metricsJetty = "com.yammer.metrics" %% "metrics-jetty" % metricsVersion val metricsLog4j = "com.yammer.metrics" %% "metrics-log4j" % metricsVersion val commonsCli = "commons-cli" % "commons-cli" % "1.2" - val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % "1.7.5" - val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.7.5" + val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % "1.7.6" + val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.7.6" /** * Logging Dependencies From 0d568cc76cf2d91163987360ed8ac09039668d72 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 5 May 2011 22:15:03 -0700 Subject: [PATCH 23/25] Added QuietErrorHandler to avoid the overly-chatty Jetty impl. --- .../dropwizard/config/ServerFactory.scala | 2 + .../dropwizard/util/QuietErrorHandler.scala | 78 +++++++++++++++++++ .../examples/HelloWorldResource.scala | 11 ++- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/com/yammer/dropwizard/util/QuietErrorHandler.scala diff --git a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala index 5aed4fc323d..fb1390e2323 100644 --- a/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala +++ b/src/main/scala/com/yammer/dropwizard/config/ServerFactory.scala @@ -11,6 +11,7 @@ import org.eclipse.jetty.servlet._ import javax.servlet.{Filter, Servlet} import java.util.EnumSet import org.eclipse.jetty.server.{DispatcherType, Server, Connector} +import com.yammer.dropwizard.util.QuietErrorHandler object ServerFactory { def provideServer(implicit config: Configuration, @@ -56,6 +57,7 @@ object ServerFactory { private def makeServer(connectors: Connector*)(implicit config: Configuration) = { val server = new Server connectors.foreach(server.addConnector) + server.addBean(new QuietErrorHandler) server.setSendServerVersion(false) server.setThreadPool(makeThreadPool) server.setStopAtShutdown(true) diff --git a/src/main/scala/com/yammer/dropwizard/util/QuietErrorHandler.scala b/src/main/scala/com/yammer/dropwizard/util/QuietErrorHandler.scala new file mode 100644 index 00000000000..d39eb24fdea --- /dev/null +++ b/src/main/scala/com/yammer/dropwizard/util/QuietErrorHandler.scala @@ -0,0 +1,78 @@ +package com.yammer.dropwizard.util + +import annotation.switch +import java.io.Writer +import javax.servlet.http.{HttpServletResponse, HttpServletRequest} +import org.eclipse.jetty.server.handler.ErrorHandler +import org.eclipse.jetty.http.HttpGenerator + +class QuietErrorHandler extends ErrorHandler { + import HttpServletResponse._ + + private def errorMessage(request: HttpServletRequest, code: Int) = + (code: @switch) match { + case SC_BAD_REQUEST => + "Your HTTP client sent a request that this server could not understand." + case SC_CONFLICT => + "The request could not be completed due to a conflict with the " + + "current state of the resource." + case SC_EXPECTATION_FAILED => + "The server could not meet the expectation given in the Expect " + + "request header." + case SC_FORBIDDEN => + "You don't have permission to access the requested resource." + case SC_GONE => + "The requested resource used to exist but no longer does." + case SC_INTERNAL_SERVER_ERROR => + "The server encountered an internal error and was unable to complete" + + " your request." + case SC_LENGTH_REQUIRED => + ("A request with the %s method requires a valid Content-Length" + + " header.").format(request.getMethod) + case SC_METHOD_NOT_ALLOWED => + ("The %s method is not allowed for the requested " + + "resouce.").format(request.getMethod) + case SC_NOT_ACCEPTABLE => + "The resource identified by the request is only capable of generating" + + " response entities which have content characteristics not" + + " acceptable according to the accept headers sent in the request." + case SC_NOT_FOUND => + "The requested resource could not be found on this server." + case SC_OK => + "" + case SC_PRECONDITION_FAILED => + "The precondition on the request for the resource failed positive" + + " evaluation." + case SC_REQUEST_ENTITY_TOO_LARGE => + ("The %s method does not allow the data transmitted, or the data" + + " volume exceeds the capacity limit.").format(request.getMethod) + case SC_REQUEST_TIMEOUT => + "The server closed the network connection because your HTTP client" + + " didn't finish the request within the specified time." + case SC_REQUEST_URI_TOO_LONG => + "The length of the requested URL exceeds the capacity limit for this" + + " server. The request cannot be processed." + case SC_REQUESTED_RANGE_NOT_SATISFIABLE => + "The server cannot serve the requested byte range." + case SC_SERVICE_UNAVAILABLE => + "The server is temporarily unable to service your request due to" + + " maintenance downtime or capacity problems. Please try again later." + case SC_UNAUTHORIZED => + "This server could not verify that you are authorized to access" + + " this resource.\n" + + "You either supplied the wrong credentials (e.g., bad password)," + + " or your HTTP client doesn't understand how to supply the" + + " required credentials." + case SC_UNSUPPORTED_MEDIA_TYPE => + "The server does not support the media type transmitted in the request." + case status => + "Your request could not be processed: " + HttpGenerator.getReasonBuffer(status) + } + + override def handleErrorPage(request: HttpServletRequest, + writer: Writer, + code: Int, + message: String) { + writer.append(errorMessage(request, code)).append("\n\n") + } +} diff --git a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala index 70a5a3ceea7..c653742b1e5 100644 --- a/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala +++ b/src/test/scala/com/yammer/dropwizard/examples/HelloWorldResource.scala @@ -1,11 +1,18 @@ package com.yammer.dropwizard.examples -import javax.ws.rs.core.MediaType -import javax.ws.rs.{QueryParam, Produces, GET, Path} +import javax.ws.rs._ +import core.Response.Status +import core.{Response, MediaType} @Path("/hello-world") @Produces(Array(MediaType.APPLICATION_JSON)) class HelloWorldResource(implicit saying: String) { @GET def sayHello(@QueryParam("opt") opt: Option[String]) = Seq(saying) + + @POST + def intentionalError = Response.status(Status.BAD_REQUEST).build() + + @PUT + def unintentionalError = None.get } From b56dd12b96e26f3431e496ca6ba909b7e288f8b6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 9 May 2011 18:53:01 -0700 Subject: [PATCH 24/25] Upgrade to Metrics 2.0.0-BETA12. --- project/build/DropwizardProject.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build/DropwizardProject.scala b/project/build/DropwizardProject.scala index 6fffb9494b8..5be9209af0a 100644 --- a/project/build/DropwizardProject.scala +++ b/project/build/DropwizardProject.scala @@ -41,7 +41,7 @@ class DropwizardProject(info: ProjectInfo) extends DefaultProject(info) */ val fig = "com.codahale" %% "fig" % "1.1.1" val jerkson = "com.codahale" %% "jerkson" % "0.1.8" - val metricsVersion = "2.0.0-BETA12-SNAPSHOT" + val metricsVersion = "2.0.0-BETA12" val metricsCore = "com.yammer.metrics" %% "metrics-core" % metricsVersion val metricsServlet = "com.yammer.metrics" %% "metrics-servlet" % metricsVersion val metricsJetty = "com.yammer.metrics" %% "metrics-jetty" % metricsVersion From a497538a02ca311c6219b160e85c9467896cc8e8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 9 May 2011 18:55:19 -0700 Subject: [PATCH 25/25] Bump version to 0.0.4. --- project/build.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/build.properties b/project/build.properties index 484ef2fbafd..874be28de2d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -2,7 +2,7 @@ #Mon Jan 17 19:25:38 PST 2011 project.organization=com.yammer project.name=dropwizard -sbt.version=0.7.5.RC0 -project.version=0.0.4-SNAPSHOT +sbt.version=0.7.6.RC0 +project.version=0.0.4 build.scala.versions=2.8.1 project.initialize=false