diff --git a/play-scala-log4j2-example/.gitignore b/play-scala-log4j2-example/.gitignore new file mode 100644 index 000000000..193873de8 --- /dev/null +++ b/play-scala-log4j2-example/.gitignore @@ -0,0 +1,10 @@ +build +logs +target +/.idea +/.idea_modules +/.classpath +/.gradle +/.project +/.settings +/RUNNING_PID diff --git a/play-scala-log4j2-example/.mergify.yml b/play-scala-log4j2-example/.mergify.yml new file mode 100644 index 000000000..32f8689ae --- /dev/null +++ b/play-scala-log4j2-example/.mergify.yml @@ -0,0 +1,27 @@ +pull_request_rules: + - name: automatic merge on CI success require review + conditions: + - status-success=Travis CI - Pull Request + - "#approved-reviews-by>=1" + - "#changes-requested-reviews-by=0" + - label!=block-merge + actions: + merge: + method: squash + strict: smart + + - name: automatic merge on CI success for TemplateControl + conditions: + - status-success=Travis CI - Pull Request + - label=merge-when-green + - label!=block-merge + actions: + merge: + method: squash + strict: smart + + - name: delete branch after merge + conditions: + - merged + actions: + delete_head_branch: {} diff --git a/play-scala-log4j2-example/.travis.yml b/play-scala-log4j2-example/.travis.yml new file mode 100644 index 000000000..1e8c0e7c4 --- /dev/null +++ b/play-scala-log4j2-example/.travis.yml @@ -0,0 +1,50 @@ +language: scala +scala: + - 2.12.8 + +before_install: + - curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | bash && . ~/.jabba/jabba.sh + +env: + global: + - JABBA_HOME=$HOME/.jabba + matrix: + # There is no concise way to specify multi-dimensional build matrix: + # https://github.com/travis-ci/travis-ci/issues/1519 + - SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.8.192-12 + - SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.11.0-1 + - SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.8.192-12 + - SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.11.0-1 + +# Exclude some combinations from build matrix. See: +# https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix +matrix: + fast_finish: true + allow_failures: + # Current release of Gradle still does not supports Play 2.7.x releases + # As soon as there is a release of Gradle that fixes that, we can then + # remove this allowed failure. + - env: SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.8.192-12 + - env: SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.11.0-1 + # Java 11 is still not fully supported. It is good that we are already + # testing our sample applications to better discover possible problems + # but we can allow failures here too. + - env: SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.11.0-1 + +install: + - $JABBA_HOME/bin/jabba install $TRAVIS_JDK + - unset _JAVA_OPTIONS + - export JAVA_HOME="$JABBA_HOME/jdk/$TRAVIS_JDK" && export PATH="$JAVA_HOME/bin:$PATH" && java -Xmx32m -version + +script: + - $SCRIPT + +before_cache: + - find $HOME/.ivy2 -name "ivydata-*.properties" -delete + - find $HOME/.sbt -name "*.lock" -delete + +cache: + directories: + - "$HOME/.ivy2/cache" + - "$HOME/.gradle/caches" + - "$HOME/.jabba/jdk" diff --git a/play-scala-log4j2-example/LICENSE b/play-scala-log4j2-example/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/play-scala-log4j2-example/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/play-scala-log4j2-example/NOTICE b/play-scala-log4j2-example/NOTICE new file mode 100644 index 000000000..6d6c034d3 --- /dev/null +++ b/play-scala-log4j2-example/NOTICE @@ -0,0 +1,8 @@ +Written by Lightbend + +To the extent possible under law, the author(s) have dedicated all copyright and +related and neighboring rights to this software to the public domain worldwide. +This software is distributed without any warranty. + +You should have received a copy of the CC0 Public Domain Dedication along with +this software. If not, see . diff --git a/play-scala-log4j2-example/README.md b/play-scala-log4j2-example/README.md new file mode 100644 index 000000000..010c1c33c --- /dev/null +++ b/play-scala-log4j2-example/README.md @@ -0,0 +1,59 @@ +# Play using Log4j 2 + +[![Build Status](https://travis-ci.org/playframework/play-scala-log4j2-example.svg?branch=2.6.x)](https://travis-ci.org/playframework/play-scala-log4j2-example) + +This is an example project showing a sample Play application that use Log4J 2 instead of using Logback. + +Please see [Using a custom logging framework](https://www.playframework.com/documentation/2.6.x/SettingsLogger#Using-a-Custom-Logging-Framework) in the Play documentation for more details. + +## Running in Production + +This application will package everything correctly when you run `sbt dist` and run the packaged script. + +There is an outstanding bug where apparently this didn't work: please add comments to if this doesn't work for you. + +## Running in Development + +You must define the `log4j.configurationFile` explicitly when the JVM is loaded or `sbt`: + +```bash +sbt -Dlog4j.configurationFile=conf/log4j2.xml +``` +Or you can set as javaOptions in `build.sbt`: +```bash +javaOptions += "-Dlog4j.configurationFile=conf/log4j2.xml" +``` +If you do not run with `log4j.configurationFile` loaded, you will see this error: + +```log +ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging. +``` + +After you define the log4j system property, running the application should look like this: + +```log +[info] Loading project definition from /Users/player/play-scala-log4j2-example/project +[info] Set current project to play-2.6-log4j2 (in build file:/Users/player/play-scala-log4j2-example/) + +No play.logger.configurator found: logging must be configured entirely by the application. +--- (Running the application, auto-reloading is enabled) --- + +[INFO ] 2017-12-20 09:41:12.268 [pool-7-thread-2] AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000 + +(Server started, use Enter to stop and go back to the console...) + +[info] Compiling 1 Scala source to /Users/player/play-scala-log4j2-example/target/scala-2.12/classes ... +[info] Done compiling. +[INFO ] 2017-12-20 09:41:41.296 [play-dev-mode-akka.actor.default-dispatcher-4] application - ApplicationTimer demo: Starting application at 2017-12-20T11:41:41.295Z. +[INFO ] 2017-12-20 09:41:41.477 [application-akka.actor.default-dispatcher-2] Slf4jLogger - Slf4jLogger started +[WARN ] 2017-12-20 09:41:41.655 [play-dev-mode-akka.actor.default-dispatcher-4] application - Using the following cache for assets +[INFO ] 2017-12-20 09:41:41.670 [play-dev-mode-akka.actor.default-dispatcher-4] Play - Application started (Dev) +```` + +Note that you will see + +```log +No play.logger.configurator found: logging must be configured entirely by the application. +``` + +when you first start it -- this is a side effect of Play's immediate reload functionality, and will not affect the application itself. You won't see the `play.logger.configurator` warning if you run the Play application in production, because there isn't a different class loader for SBT vs for the Play application. diff --git a/play-scala-log4j2-example/app/Filters.scala b/play-scala-log4j2-example/app/Filters.scala new file mode 100644 index 000000000..a72db50dc --- /dev/null +++ b/play-scala-log4j2-example/app/Filters.scala @@ -0,0 +1,33 @@ +import javax.inject._ +import play.api._ +import play.api.http.HttpFilters +import play.api.mvc._ + +import filters.ExampleFilter + +/** + * This class configures filters that run on every request. This + * class is queried by Play to get a list of filters. + * + * Play will automatically use filters from any class called + * `Filters` that is placed the root package. You can load filters + * from a different class by adding a `play.http.filters` setting to + * the `application.conf` configuration file. + * + * @param env Basic environment settings for the current application. + * @param exampleFilter A demonstration filter that adds a header to + * each response. + */ +@Singleton +class Filters @Inject() ( + env: Environment, + exampleFilter: ExampleFilter) extends HttpFilters { + + override val filters = { + // Use the example filter if we're running development mode. If + // we're running in production or test mode then don't use any + // filters at all. + if (env.mode == Mode.Dev) Seq(exampleFilter) else Seq.empty + } + +} diff --git a/play-scala-log4j2-example/app/Log4J2LoggerConfigurator.scala b/play-scala-log4j2-example/app/Log4J2LoggerConfigurator.scala new file mode 100644 index 000000000..0b9a83441 --- /dev/null +++ b/play-scala-log4j2-example/app/Log4J2LoggerConfigurator.scala @@ -0,0 +1,45 @@ +import java.io.File +import java.net.URL + +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.core.LoggerContext +import org.apache.logging.log4j.core.config.Configurator +import org.slf4j.ILoggerFactory +import play.api.{Configuration, Environment, LoggerConfigurator, Mode} + +class Log4J2LoggerConfigurator extends LoggerConfigurator { + + private lazy val factory: ILoggerFactory = org.slf4j.impl.StaticLoggerBinder.getSingleton.getLoggerFactory + + override def init(rootPath: File, mode: Mode): Unit = { + val properties = Map("application.home" -> rootPath.getAbsolutePath) + val resourceName = "log4j2.xml" + val resourceUrl = Option(this.getClass.getClassLoader.getResource(resourceName)) + configure(properties, resourceUrl) + } + + override def shutdown(): Unit = { + val context = LogManager.getContext().asInstanceOf[LoggerContext] + Configurator.shutdown(context) + } + + override def configure(env: Environment): Unit = { + val properties = LoggerConfigurator.generateProperties(env, Configuration.empty, Map.empty) + val resourceUrl = env.resource("log4j2.xml") + configure(properties, resourceUrl) + } + + override def configure(env: Environment, configuration: Configuration, optionalProperties: Map[String, String]): Unit = { + // LoggerConfigurator.generateProperties enables play.logger.includeConfigProperties=true + val properties = LoggerConfigurator.generateProperties(env, configuration, optionalProperties) + val resourceUrl = env.resource("log4j2.xml") + configure(properties, resourceUrl) + } + + override def configure(properties: Map[String, String], config: Option[URL]): Unit = { + val context = LogManager.getContext(false).asInstanceOf[LoggerContext] + context.setConfigLocation(config.get.toURI) + } + + override def loggerFactory: ILoggerFactory = factory +} \ No newline at end of file diff --git a/play-scala-log4j2-example/app/Module.scala b/play-scala-log4j2-example/app/Module.scala new file mode 100644 index 000000000..27d5f4b20 --- /dev/null +++ b/play-scala-log4j2-example/app/Module.scala @@ -0,0 +1,28 @@ +import com.google.inject.AbstractModule +import java.time.Clock + +import services.{ApplicationTimer, AtomicCounter, Counter} + +/** + * This class is a Guice module that tells Guice how to bind several + * different types. This Guice module is created when the Play + * application starts. + + * Play will automatically use any class called `Module` that is in + * the root package. You can create modules in other locations by + * adding `play.modules.enabled` settings to the `application.conf` + * configuration file. + */ +class Module extends AbstractModule { + + override def configure() = { + // Use the system clock as the default implementation of Clock + bind(classOf[Clock]).toInstance(Clock.systemDefaultZone) + // Ask Guice to create an instance of ApplicationTimer when the + // application starts. + bind(classOf[ApplicationTimer]).asEagerSingleton + // Set AtomicCounter as the implementation for Counter. + bind(classOf[Counter]).to(classOf[AtomicCounter]) + } + +} diff --git a/play-scala-log4j2-example/app/controllers/AsyncController.scala b/play-scala-log4j2-example/app/controllers/AsyncController.scala new file mode 100644 index 000000000..112eca674 --- /dev/null +++ b/play-scala-log4j2-example/app/controllers/AsyncController.scala @@ -0,0 +1,41 @@ +package controllers + +import akka.actor.ActorSystem +import javax.inject._ +import play.api._ +import play.api.mvc._ +import scala.concurrent.{ExecutionContext, Future, Promise} +import scala.concurrent.duration._ + +/** + * This controller creates an `Action` that demonstrates how to write + * simple asychronous code in a controller. It uses a timer to + * asynchronously delay sending a response for 1 second. + * + * @param actorSystem We need the `ActorSystem`'s `Scheduler` to + * run code after a delay. + * @param exec We need an `ExecutionContext` to execute our + * asynchronous code. + */ +@Singleton +class AsyncController @Inject() (val controllerComponents: ControllerComponents, actorSystem: ActorSystem)(implicit exec: ExecutionContext) extends BaseController { + + /** + * Create an Action that returns a plain text message after a delay + * of 1 second. + * + * The configuration in the `routes` file means that this method + * will be called when the application receives a `GET` request with + * a path of `/message`. + */ + def message = Action.async { + getFutureMessage(1.second).map { msg => Ok(msg) } + } + + private def getFutureMessage(delayTime: FiniteDuration): Future[String] = { + val promise: Promise[String] = Promise[String]() + actorSystem.scheduler.scheduleOnce(delayTime) { promise.success("Hi!") } + promise.future + } + +} diff --git a/play-scala-log4j2-example/app/controllers/CountController.scala b/play-scala-log4j2-example/app/controllers/CountController.scala new file mode 100644 index 000000000..29ede4f92 --- /dev/null +++ b/play-scala-log4j2-example/app/controllers/CountController.scala @@ -0,0 +1,25 @@ +package controllers + +import javax.inject._ +import play.api._ +import play.api.mvc._ + +import services.Counter + +/** + * This controller demonstrates how to use dependency injection to + * bind a component into a controller class. The class creates an + * `Action` that shows an incrementing count to users. The [[Counter]] + * object is injected by the Guice dependency injection system. + */ +@Singleton +class CountController @Inject() (val controllerComponents: ControllerComponents, counter: Counter) extends BaseController { + + /** + * Create an action that responds with the [[Counter]]'s current + * count. The result is plain text. This `Action` is mapped to + * `GET /count` requests by an entry in the `routes` config file. + */ + def count = Action { Ok(counter.nextCount().toString) } + +} diff --git a/play-scala-log4j2-example/app/controllers/HomeController.scala b/play-scala-log4j2-example/app/controllers/HomeController.scala new file mode 100644 index 000000000..3bf63461a --- /dev/null +++ b/play-scala-log4j2-example/app/controllers/HomeController.scala @@ -0,0 +1,24 @@ +package controllers + +import javax.inject._ +import play.api._ +import play.api.mvc._ + +/** + * This controller creates an `Action` to handle HTTP requests to the + * application's home page. + */ +@Singleton +class HomeController @Inject() (val controllerComponents: ControllerComponents) extends BaseController { + + /** + * Create an Action to render an HTML page with a welcome message. + * The configuration in the `routes` file means that this method + * will be called when the application receives a `GET` request with + * a path of `/`. + */ + def index = Action { + Ok(views.html.index("Your new application is ready.")) + } + +} diff --git a/play-scala-log4j2-example/app/filters/ExampleFilter.scala b/play-scala-log4j2-example/app/filters/ExampleFilter.scala new file mode 100644 index 000000000..e2f3c1eb3 --- /dev/null +++ b/play-scala-log4j2-example/app/filters/ExampleFilter.scala @@ -0,0 +1,33 @@ +package filters + +import akka.stream.Materializer +import javax.inject._ +import play.api.mvc._ +import scala.concurrent.{ExecutionContext, Future} + +/** + * This is a simple filter that adds a header to all requests. It's + * added to the application's list of filters by the + * [[ExampleFilter]] class. + * + * @param mat This object is needed to handle streaming of requests + * and responses. + * @param exec This class is needed to execute code asynchronously. + * It is used below by the `map` method. + */ +@Singleton +class ExampleFilter @Inject()( + implicit override val mat: Materializer, + exec: ExecutionContext) extends Filter { + + override def apply(nextFilter: RequestHeader => Future[Result]) + (requestHeader: RequestHeader): Future[Result] = { + // Run the next filter in the chain. This will call other filters + // and eventually call the action. Take the result and modify it + // by adding a new header. + nextFilter(requestHeader).map { result => + result.withHeaders("X-ExampleFilter" -> "foo") + } + } + +} diff --git a/play-scala-log4j2-example/app/services/ApplicationTimer.scala b/play-scala-log4j2-example/app/services/ApplicationTimer.scala new file mode 100644 index 000000000..d893c7742 --- /dev/null +++ b/play-scala-log4j2-example/app/services/ApplicationTimer.scala @@ -0,0 +1,39 @@ +package services + +import java.time.{Clock, Instant} +import javax.inject._ +import play.api.Logger +import play.api.inject.ApplicationLifecycle +import scala.concurrent.Future + +/** + * This class demonstrates how to run code when the + * application starts and stops. It starts a timer when the + * application starts. When the application stops it prints out how + * long the application was running for. + * + * This class is registered for Guice dependency injection in the + * [[Module]] class. We want the class to start when the application + * starts, so it is registered as an "eager singleton". See the code + * in the [[Module]] class to see how this happens. + * + * This class needs to run code when the server stops. It uses the + * application's [[ApplicationLifecycle]] to register a stop hook. + */ +@Singleton +class ApplicationTimer @Inject() (clock: Clock, appLifecycle: ApplicationLifecycle) { + + // This code is called when the application starts. + private val start: Instant = clock.instant + Logger.info(s"ApplicationTimer demo: Starting application at $start.") + + // When the application starts, register a stop hook with the + // ApplicationLifecyle object. The code inside the stop hook wil + // be run when the application stops. + appLifecycle.addStopHook { () => + val stop: Instant = clock.instant + val runningTime: Long = stop.getEpochSecond - start.getEpochSecond + Logger.info(s"ApplicationTimer demo: Stopping application at ${clock.instant} after ${runningTime}s.") + Future.successful(()) + } +} diff --git a/play-scala-log4j2-example/app/services/Counter.scala b/play-scala-log4j2-example/app/services/Counter.scala new file mode 100644 index 000000000..fe19334ee --- /dev/null +++ b/play-scala-log4j2-example/app/services/Counter.scala @@ -0,0 +1,29 @@ +package services + +import java.util.concurrent.atomic.AtomicInteger +import javax.inject._ + +/** + * This trait demonstrates how to create a component that is injected + * into a controller. The trait represents a counter that returns a + * incremented number each time it is called. + */ +trait Counter { + def nextCount(): Int +} + +/** + * This class is a concrete implementation of the [[Counter]] trait. + * It is configured for Guice dependency injection in the [[Module]] + * class. + * + * This class has a `Singleton` annotation because we need to make + * sure we only use one counter per application. Without this + * annotation we would get a new instance every time a [[Counter]] is + * injected. + */ +@Singleton +class AtomicCounter extends Counter { + private val atomicCounter = new AtomicInteger() + override def nextCount(): Int = atomicCounter.getAndIncrement() +} diff --git a/play-scala-log4j2-example/app/views/index.scala.html b/play-scala-log4j2-example/app/views/index.scala.html new file mode 100644 index 000000000..e5acbe68e --- /dev/null +++ b/play-scala-log4j2-example/app/views/index.scala.html @@ -0,0 +1,15 @@ +@* + * This template takes a single argument, a String containing a + * message to display. + *@ +@(message: String) + +@* + * Call the the `main` template with two arguments. The first + * argument is a `String` with the title of the page, the second + * argument is an `Html` object containing the body of the page. + *@ +@main("Welcome to Play") { +

Welcome to PlayFramework!

+

Your new application is ready.

+} diff --git a/play-scala-log4j2-example/app/views/main.scala.html b/play-scala-log4j2-example/app/views/main.scala.html new file mode 100644 index 000000000..9414f4be6 --- /dev/null +++ b/play-scala-log4j2-example/app/views/main.scala.html @@ -0,0 +1,23 @@ +@* + * This template is called from the `index` template. This template + * handles the rendering of the page header and body tags. It takes + * two arguments, a `String` for the title of the page and an `Html` + * object to insert into the body of the page. + *@ +@(title: String)(content: Html) + + + + + @* Here's where we render the page title `String`. *@ + @title + + + + + + @* And here's where we render the `Html` object containing + * the page content. *@ + @content + + diff --git a/play-scala-log4j2-example/build.gradle b/play-scala-log4j2-example/build.gradle new file mode 100644 index 000000000..351f75e44 --- /dev/null +++ b/play-scala-log4j2-example/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'play' + id 'idea' +} + +def playVersion = "2.6.21" +def log4jVersion = '2.10.0' +def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") + +model { + components { + play { + platform play: playVersion, scala: scalaVersion, java: '1.8' + injectedRoutesGenerator = true + + sources { + twirlTemplates { + defaultImports = TwirlImports.SCALA + } + } + } + } +} + +dependencies { + play "com.typesafe.play:play-guice_$scalaVersion:$playVersion" + + play "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion" + play "org.apache.logging.log4j:log4j-api:$log4jVersion" + play "org.apache.logging.log4j:log4j-core:$log4jVersion" + + playTest "org.scalatestplus.play:scalatestplus-play_$scalaVersion:3.1.2" +} + +repositories { + jcenter() + maven { + name "lightbend-maven-releases" + url "https://repo.lightbend.com/lightbend/maven-release" + } + ivy { + name "lightbend-ivy-release" + url "https://repo.lightbend.com/lightbend/ivy-releases" + layout "ivy" + } +} diff --git a/play-scala-log4j2-example/build.sbt b/play-scala-log4j2-example/build.sbt new file mode 100644 index 000000000..0911321ab --- /dev/null +++ b/play-scala-log4j2-example/build.sbt @@ -0,0 +1,26 @@ + +name := """play-2.6-log4j2""" + +version := "1.0-SNAPSHOT" + +val log4jVersion = "2.10.0" + +// Run with activator -Dlog4j.configurationFile=conf/log4j2.xml +lazy val root = (project in file(".")) + .enablePlugins(PlayScala) + .disablePlugins(PlayLogback).settings( + libraryDependencies ++= Seq( + guice, + "org.apache.logging.log4j" % "log4j-slf4j-impl" % log4jVersion, + "org.apache.logging.log4j" % "log4j-api" % log4jVersion, + "org.apache.logging.log4j" % "log4j-core" % log4jVersion + ) +) + +scalaVersion in ThisBuild := "2.12.4" + +crossScalaVersions := Seq("2.11.12", "2.12.4") + +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test + +javaOptions += "-Dlog4j.configurationFile=conf/log4j2.xml" diff --git a/play-scala-log4j2-example/conf/application.conf b/play-scala-log4j2-example/conf/application.conf new file mode 100644 index 000000000..6355f43b3 --- /dev/null +++ b/play-scala-log4j2-example/conf/application.conf @@ -0,0 +1,345 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other subsitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key = "changeme" + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to disable, you can list them here. + #enabled += my.application.Module + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" +} + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Filters + # https://www.playframework.com/documentation/latest/ScalaHttpFilters + # https://www.playframework.com/documentation/latest/JavaHttpFilters + # ~~~~~ + # Filters run code on every request. They can be used to perform + # common logic for all your actions, e.g. adding common headers. + # Defaults to "Filters" in the root package (aka "apps" folder) + # Alternatively you can explicitly register a class here. + #filters += my.application.Filters + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + #log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + #transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filters +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. You must enable this by adding to build.sbt: +# +# libraryDependencies += filters +# +play.filters { + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## CSRF Filter + # https://www.playframework.com/documentation/latest/ScalaCsrf#Applying-a-global-CSRF-filter + # https://www.playframework.com/documentation/latest/JavaCsrf#Applying-a-global-CSRF-filter + # ~~~~~ + # Play supports multiple methods for verifying that a request is not a CSRF request. + # The primary mechanism is a CSRF token. This token gets placed either in the query string + # or body of every form submitted, and also gets placed in the users session. + # Play then verifies that both tokens are present and match. + csrf { + # Sets the cookie to be sent only over HTTPS + #cookie.secure = true + + # Defaults to CSRFErrorHandler in the root package. + #errorHandler = MyCSRFErrorHandler + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +## Evolutions +# https://www.playframework.com/documentation/latest/Evolutions +# ~~~~~ +# Evolutions allows database scripts to be automatically run on startup in dev mode +# for database migrations. You must enable this by adding to build.sbt: +# +# libraryDependencies += evolutions +# +play.evolutions { + # You can disable evolutions for a specific datasource if necessary + #db.default.enabled = false +} + +## Database Connection Pool +# https://www.playframework.com/documentation/latest/SettingsJDBC +# ~~~~~ +# Play doesn't require a JDBC database to run, but you can easily enable one. +# +# libraryDependencies += jdbc +# +play.db { + # The combination of these two settings results in "db.default" as the + # default JDBC pool: + #config = "db" + #default = "default" + + # Play uses HikariCP as the default connection pool. You can override + # settings by changing the prototype: + prototype { + # Sets a fixed JDBC connection pool size of 50 + #hikaricp.minimumIdle = 50 + #hikaricp.maximumPoolSize = 50 + } +} + +## JDBC Datasource +# https://www.playframework.com/documentation/latest/JavaDatabase +# https://www.playframework.com/documentation/latest/ScalaDatabase +# ~~~~~ +# Once JDBC datasource is set up, you can work with several different +# database options: +# +# Slick (Scala preferred option): https://www.playframework.com/documentation/latest/PlaySlick +# JPA (Java preferred option): https://playframework.com/documentation/latest/JavaJPA +# EBean: https://playframework.com/documentation/latest/JavaEbean +# Anorm: https://www.playframework.com/documentation/latest/ScalaAnorm +# +db { + # You can declare as many datasources as you want. + # By convention, the default datasource is named `default` + + # https://www.playframework.com/documentation/latest/Developing-with-the-H2-Database + #default.driver = org.h2.Driver + #default.url = "jdbc:h2:mem:play" + #default.username = sa + #default.password = "" + + # You can turn on SQL logging for any datasource + # https://www.playframework.com/documentation/latest/Highlights25#Logging-SQL-statements + #default.logSql=true +} diff --git a/play-scala-log4j2-example/conf/log4j2.xml b/play-scala-log4j2-example/conf/log4j2.xml new file mode 100644 index 000000000..7b7abda05 --- /dev/null +++ b/play-scala-log4j2-example/conf/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/play-scala-log4j2-example/conf/logger-configurator.properties b/play-scala-log4j2-example/conf/logger-configurator.properties new file mode 100644 index 000000000..b6645a4fc --- /dev/null +++ b/play-scala-log4j2-example/conf/logger-configurator.properties @@ -0,0 +1 @@ +play.logger.configurator=Log4J2LoggerConfigurator diff --git a/play-scala-log4j2-example/conf/routes b/play-scala-log4j2-example/conf/routes new file mode 100644 index 000000000..cc707d49d --- /dev/null +++ b/play-scala-log4j2-example/conf/routes @@ -0,0 +1,13 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +# An example controller showing a sample home page +GET / controllers.HomeController.index +# An example controller showing how to use dependency injection +GET /count controllers.CountController.count +# An example controller showing how to write asynchronous code +GET /message controllers.AsyncController.message + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) diff --git a/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.jar b/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..01b8bf6b1 Binary files /dev/null and b/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.jar differ diff --git a/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.properties b/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..89dba2d9d --- /dev/null +++ b/play-scala-log4j2-example/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/play-scala-log4j2-example/gradlew b/play-scala-log4j2-example/gradlew new file mode 100755 index 000000000..cccdd3d51 --- /dev/null +++ b/play-scala-log4j2-example/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/play-scala-log4j2-example/gradlew.bat b/play-scala-log4j2-example/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/play-scala-log4j2-example/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/play-scala-log4j2-example/project/build.properties b/play-scala-log4j2-example/project/build.properties new file mode 100644 index 000000000..c0bab0494 --- /dev/null +++ b/play-scala-log4j2-example/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.8 diff --git a/play-scala-log4j2-example/project/plugins.sbt b/play-scala-log4j2-example/project/plugins.sbt new file mode 100644 index 000000000..372755e6f --- /dev/null +++ b/play-scala-log4j2-example/project/plugins.sbt @@ -0,0 +1,2 @@ +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.21") diff --git a/play-scala-log4j2-example/public/images/favicon.png b/play-scala-log4j2-example/public/images/favicon.png new file mode 100644 index 000000000..c7d92d2ae Binary files /dev/null and b/play-scala-log4j2-example/public/images/favicon.png differ diff --git a/play-scala-log4j2-example/public/javascripts/hello.js b/play-scala-log4j2-example/public/javascripts/hello.js new file mode 100644 index 000000000..02ee13c7c --- /dev/null +++ b/play-scala-log4j2-example/public/javascripts/hello.js @@ -0,0 +1,3 @@ +if (window.console) { + console.log("Welcome to your Play application's JavaScript!"); +} diff --git a/play-scala-log4j2-example/public/stylesheets/main.css b/play-scala-log4j2-example/public/stylesheets/main.css new file mode 100644 index 000000000..e69de29bb diff --git a/play-scala-log4j2-example/scripts/script-helper b/play-scala-log4j2-example/scripts/script-helper new file mode 100644 index 000000000..9a2faa643 --- /dev/null +++ b/play-scala-log4j2-example/scripts/script-helper @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +java_version=$(java -version 2>&1 | java -version 2>&1 | awk -F '"' '/version/ {print $2}') + +if [[ $java_version = 1.8* ]] ; then + echo "The build is using Java 8 ($java_version). No addional JVM params needed." +else + echo "The build is using Java 9+ ($java_version). We need additional JVM parameters" + export _JAVA_OPTIONS="$_JAVA_OPTIONS --add-modules=java.xml.bind" +fi diff --git a/play-scala-log4j2-example/scripts/test-gradle b/play-scala-log4j2-example/scripts/test-gradle new file mode 100755 index 000000000..84a051a20 --- /dev/null +++ b/play-scala-log4j2-example/scripts/test-gradle @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/script-helper" + +# Using cut because TRAVIS_SCALA_VERSION is the full Scala +# version (for example 2.12.4), but Gradle expects just the +# binary version (for example 2.12) +scala_binary_version=$(echo $TRAVIS_SCALA_VERSION | cut -c1-4) + +echo "+------------------------------+" +echo "| Executing tests using Gradle |" +echo "+------------------------------+" +./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace diff --git a/play-scala-log4j2-example/scripts/test-sbt b/play-scala-log4j2-example/scripts/test-sbt new file mode 100755 index 000000000..0425367b1 --- /dev/null +++ b/play-scala-log4j2-example/scripts/test-sbt @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/script-helper" + +echo "+----------------------------+" +echo "| Executing tests using sbt |" +echo "+----------------------------+" +sbt ++$TRAVIS_SCALA_VERSION test diff --git a/play-scala-log4j2-example/test/ApplicationSpec.scala b/play-scala-log4j2-example/test/ApplicationSpec.scala new file mode 100644 index 000000000..ee82bf4ef --- /dev/null +++ b/play-scala-log4j2-example/test/ApplicationSpec.scala @@ -0,0 +1,43 @@ +import org.scalatestplus.play._ +import org.scalatestplus.play.guice.GuiceOneAppPerTest +import play.api.test._ +import play.api.test.Helpers._ + +/** + * Add your spec here. + * You can mock out a whole application including requests, plugins etc. + * For more information, consult the wiki. + */ +class ApplicationSpec extends PlaySpec with GuiceOneAppPerTest { + + "Routes" should { + + "send 404 on a bad request" in { + route(app, FakeRequest(GET, "/boum")).map(status) mustBe Some(NOT_FOUND) + } + + } + + "HomeController" should { + + "render the index page" in { + val home = route(app, FakeRequest(GET, "/")).get + + status(home) mustBe OK + contentType(home) mustBe Some("text/html") + contentAsString(home) must include ("Your new application is ready.") + } + + } + + "CountController" should { + + "return an increasing count" in { + contentAsString(route(app, FakeRequest(GET, "/count")).get) mustBe "0" + contentAsString(route(app, FakeRequest(GET, "/count")).get) mustBe "1" + contentAsString(route(app, FakeRequest(GET, "/count")).get) mustBe "2" + } + + } + +} diff --git a/play-scala-log4j2-example/test/IntegrationSpec.scala b/play-scala-log4j2-example/test/IntegrationSpec.scala new file mode 100644 index 000000000..40ebf7e12 --- /dev/null +++ b/play-scala-log4j2-example/test/IntegrationSpec.scala @@ -0,0 +1,19 @@ +import org.scalatestplus.play._ +import org.scalatestplus.play.guice.GuiceOneServerPerTest + +/** + * add your integration spec here. + * An integration test will fire up a whole play application in a real (or headless) browser + */ +class IntegrationSpec extends PlaySpec with GuiceOneServerPerTest with OneBrowserPerTest with HtmlUnitFactory { + + "Application" should { + + "work from within a browser" in { + + go to ("http://localhost:" + port) + + pageSource must include ("Your new application is ready.") + } + } +}