├── inject ├── sample │ ├── public │ │ ├── stylesheets │ │ │ └── main.css │ │ └── images │ │ │ └── favicon.png │ ├── project │ │ ├── build.properties │ │ ├── plugins.sbt │ │ └── Build.scala │ ├── app │ │ ├── views │ │ │ └── index.scala.html │ │ ├── service │ │ │ ├── Something.java │ │ │ └── MyService.java │ │ ├── controllers │ │ │ └── Application.java │ │ ├── Global.java │ │ └── module │ │ │ └── Dependencies.java │ └── conf │ │ ├── play.plugins │ │ ├── routes │ │ └── application.conf ├── project │ ├── build.properties │ └── Build.scala ├── sample_without_static_field │ ├── public │ │ ├── stylesheets │ │ │ └── main.css │ │ └── images │ │ │ └── favicon.png │ ├── project │ │ ├── build.properties │ │ ├── plugins.sbt │ │ └── Build.scala │ ├── app │ │ ├── views │ │ │ └── index.scala.html │ │ ├── service │ │ │ ├── Something.java │ │ │ └── MyService.java │ │ ├── Global.java │ │ ├── controllers │ │ │ └── Application.java │ │ └── module │ │ │ └── Dependencies.java │ └── conf │ │ ├── play.plugins │ │ ├── routes │ │ └── application.conf ├── src │ └── main │ │ └── java │ │ └── com │ │ ├── typesafe │ │ └── plugin │ │ │ └── inject │ │ │ ├── ManualInjectionPlugin.java │ │ │ ├── InjectPlugin.java │ │ │ └── Helper.java │ │ └── google │ │ └── inject │ │ └── mini │ │ └── MiniGuice.java └── README.md ├── dust ├── sample │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ ├── Build.scala │ │ └── plugins.sbt │ ├── app │ │ ├── assets │ │ │ └── example.tl │ │ ├── controllers │ │ │ └── Application.java │ │ └── views │ │ │ └── index.scala.html │ ├── public │ │ ├── images │ │ │ └── favicon.png │ │ └── javascripts │ │ │ ├── dust-core-0.3.0.min.js │ │ │ ├── dust-core-0.6.0.min.js │ │ │ └── dust-core-0.6.0.js │ └── conf │ │ ├── routes │ │ └── application.conf ├── project │ └── build.properties ├── src │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── typesafe │ │ │ └── plugin │ │ │ ├── DustKeys.scala │ │ │ ├── DustPlugin.scala │ │ │ └── DustTasks.scala │ └── test │ │ └── scala │ │ └── com │ │ └── typesafe │ │ └── plugin │ │ └── DustTasksTest.scala ├── build.sbt └── README.md ├── redis ├── project │ ├── build.properties │ └── plugins.sbt ├── version.sbt ├── src │ ├── main │ │ ├── resources │ │ │ └── reference.conf │ │ └── scala │ │ │ └── com │ │ │ └── typesafe │ │ │ └── play │ │ │ └── redis │ │ │ ├── SedisPoolProvider.scala │ │ │ ├── JedisPoolProvider.scala │ │ │ ├── RedisModule.scala │ │ │ └── RedisCacheApi.scala │ └── test │ │ └── scala │ │ └── com │ │ └── typesafe │ │ └── play │ │ └── redis │ │ └── RedisCacheApiSpec.scala ├── build.sbt └── README.md ├── sbtlogger ├── project │ └── build.properties ├── build.sbt ├── README.md └── src │ └── main │ └── scala │ └── com │ └── typesafe │ └── util │ └── Sbt.scala ├── util ├── project │ └── build.properties ├── build.sbt ├── README.md └── src │ └── main │ └── scala │ └── com │ └── typesafe │ └── plugin │ └── package.scala ├── sbtgoodies ├── project │ └── build.properties ├── sample │ ├── project │ │ ├── build.properties │ │ ├── plugins.sbt │ │ └── Build.scala │ └── conf │ │ ├── routes │ │ └── application.conf ├── src │ └── main │ │ └── scala │ │ └── com │ │ └── typesafe │ │ └── plugin │ │ ├── SbtGoodiesKeys.scala │ │ ├── SbtGoodiesPlugin.scala │ │ └── sbtGoodiesTasks.scala ├── build.sbt └── README.md ├── statsd ├── README.textile ├── project │ ├── build.properties │ └── plugins.sbt ├── .gitignore ├── sample │ └── sample-statsd │ │ ├── app │ │ ├── Global.scala │ │ └── controllers │ │ │ └── Application.scala │ │ ├── conf │ │ ├── application.conf │ │ └── routes │ │ └── test │ │ └── IntegrationTest.scala ├── src │ ├── main │ │ ├── scala │ │ │ └── play │ │ │ │ └── modules │ │ │ │ └── statsd │ │ │ │ └── api │ │ │ │ ├── Sugar.scala │ │ │ │ ├── StatsdFilter.scala │ │ │ │ ├── StatsdClientCake.scala │ │ │ │ └── StatsdClient.scala │ │ └── java │ │ │ └── play │ │ │ └── modules │ │ │ └── statsd │ │ │ ├── StatsdFilter.java │ │ │ └── Statsd.java │ └── test │ │ ├── java │ │ └── play │ │ │ └── modules │ │ │ └── statsd │ │ │ └── StatsdTest.java │ │ └── scala │ │ └── play │ │ └── modules │ │ └── statsd │ │ └── api │ │ └── StatsdSpec.scala ├── build.sbt └── documentation │ └── manual │ └── home.textile ├── mailer └── README.md ├── run-tests-travis.sh ├── .gitignore ├── .travis.yml ├── README.md └── guice └── README.md /inject/sample/public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dust/sample/build.sbt: -------------------------------------------------------------------------------- 1 | name := "dust-sample" 2 | -------------------------------------------------------------------------------- /dust/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.12.0 2 | -------------------------------------------------------------------------------- /inject/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.11.3 -------------------------------------------------------------------------------- /redis/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /sbtlogger/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.11.3 -------------------------------------------------------------------------------- /util/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.5 2 | -------------------------------------------------------------------------------- /sbtgoodies/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.12.0 2 | -------------------------------------------------------------------------------- /statsd/README.textile: -------------------------------------------------------------------------------- 1 | documentation/manual/home.textile -------------------------------------------------------------------------------- /statsd/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.5 2 | -------------------------------------------------------------------------------- /dust/sample/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.11.3 2 | -------------------------------------------------------------------------------- /inject/sample/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.11.3 2 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /redis/version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "2.5.1-SNAPSHOT" 2 | -------------------------------------------------------------------------------- /sbtgoodies/sample/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.12.0 2 | -------------------------------------------------------------------------------- /dust/sample/app/assets/example.tl: -------------------------------------------------------------------------------- 1 | Hello {name}! You have {count} new messages. -------------------------------------------------------------------------------- /statsd/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | project/boot/ 3 | project/target/ 4 | logs/ 5 | -------------------------------------------------------------------------------- /inject/sample/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @message 4 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.11.3 2 | -------------------------------------------------------------------------------- /inject/sample/conf/play.plugins: -------------------------------------------------------------------------------- 1 | 1500:com.typesafe.plugin.inject.ManualInjectionPlugin 2 | -------------------------------------------------------------------------------- /mailer/README.md: -------------------------------------------------------------------------------- 1 | # This project has moved to https://github.com/playframework/play-mailer 2 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @message 4 | 5 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/conf/play.plugins: -------------------------------------------------------------------------------- 1 | 1500:com.typesafe.plugin.inject.ManualInjectionPlugin 2 | -------------------------------------------------------------------------------- /dust/sample/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playframework/play-plugins/HEAD/dust/sample/public/images/favicon.png -------------------------------------------------------------------------------- /inject/sample/app/service/Something.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | public interface Something { 4 | 5 | public String noWhat(); 6 | 7 | } -------------------------------------------------------------------------------- /inject/sample/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playframework/play-plugins/HEAD/inject/sample/public/images/favicon.png -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/service/Something.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | public interface Something { 4 | 5 | public String noWhat(); 6 | 7 | } -------------------------------------------------------------------------------- /inject/sample_without_static_field/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playframework/play-plugins/HEAD/inject/sample_without_static_field/public/images/favicon.png -------------------------------------------------------------------------------- /statsd/sample/sample-statsd/app/Global.scala: -------------------------------------------------------------------------------- 1 | import play.api.mvc.WithFilters 2 | import play.modules.statsd.api.StatsdFilter 3 | 4 | object Global extends WithFilters(new StatsdFilter) 5 | -------------------------------------------------------------------------------- /redis/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.3") 2 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") 3 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0") 4 | -------------------------------------------------------------------------------- /run-tests-travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | if [ "$PROJECT" = "redis" ] 4 | then 5 | cd util && sbt +publish-local && cd ../$PROJECT && sbt test 6 | else 7 | cd util && sbt +publish-local && cd ../$PROJECT && sbt +test 8 | fi 9 | -------------------------------------------------------------------------------- /sbtgoodies/src/main/scala/com/typesafe/plugin/SbtGoodiesKeys.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin 2 | 3 | import sbt._ 4 | 5 | trait SbtGoodiesKeys { 6 | val distUnzip = TaskKey[Unit]("dist-unzip", "unzip the standalone application package") 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | repository/ 4 | dist/ 5 | *.lock 6 | *.komodoproject 7 | .DS_Store 8 | project/boot/ 9 | framework/project/boot/ 10 | documentation/api 11 | workspace/ 12 | framework/sbt/boot 13 | .history 14 | .idea 15 | RUNNING_PID 16 | -------------------------------------------------------------------------------- /inject/sample/app/service/MyService.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | public abstract class MyService { 4 | 5 | protected Something s; 6 | 7 | public MyService(Something s) { 8 | this.s = s; 9 | } 10 | 11 | abstract public String demonstrate(); 12 | } -------------------------------------------------------------------------------- /statsd/sample/sample-statsd/conf/application.conf: -------------------------------------------------------------------------------- 1 | 2 | application.secret="h:ueBbICA:P37;UIGe5ib>VSN@14UAag/e6t>_AtZ[uiZ?Ae/]QTLN?vfSSyOv2" 3 | application.langs="en" 4 | 5 | statsd.enabled=true 6 | statsd.host=localhost 7 | statsd.port=8125 8 | statsd.stat.prefix=statsd-sample 9 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/service/MyService.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | public abstract class MyService { 4 | 5 | protected Something s; 6 | 7 | public MyService(Something s) { 8 | this.s = s; 9 | } 10 | 11 | abstract public String demonstrate(); 12 | } -------------------------------------------------------------------------------- /sbtgoodies/sample/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Map static resources from the /public folder to the /assets URL path 6 | GET /assets/*file controllers.Assets.at(path="/public", file) 7 | 8 | -------------------------------------------------------------------------------- /inject/sample/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("play" % "sbt-plugin" % "2.0.1") 9 | -------------------------------------------------------------------------------- /redis/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | play { 2 | 3 | modules { 4 | enabled += "com.typesafe.play.redis.RedisModule" 5 | } 6 | 7 | cache { 8 | # The name of the default cache to use in redis 9 | defaultCache = "play" 10 | 11 | redis { 12 | # The caches to bind 13 | bindCaches = [] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /statsd/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | // logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.0") 9 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("play" % "sbt-plugin" % "2.0.1") 9 | -------------------------------------------------------------------------------- /inject/sample/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index() 7 | 8 | # Map static resources from the /public folder to the /assets URL path 9 | GET /assets/*file controllers.Assets.at(path="/public", file) 10 | -------------------------------------------------------------------------------- /sbtgoodies/src/main/scala/com/typesafe/plugin/SbtGoodiesPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin 2 | 3 | import sbt._ 4 | import Keys._ 5 | 6 | object SbtGoodiesPlugin extends Plugin with SbtGoodiesTasks { 7 | 8 | val distUnzipSettings: Seq[Setting[_]] = Seq( 9 | distUnzip <<= distUnzipTask, 10 | distUnzip <<= distUnzip.dependsOn(PlayProject.dist) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / module.Dependencies.application.index 7 | 8 | # Map static resources from the /public folder to the /assets URL path 9 | GET /assets/*file controllers.Assets.at(path="/public", file) 10 | -------------------------------------------------------------------------------- /inject/sample/app/controllers/Application.java: -------------------------------------------------------------------------------- 1 | package controllers; 2 | 3 | import play.*; 4 | import play.mvc.*; 5 | 6 | import views.html.*; 7 | 8 | import javax.inject.*; 9 | import service.*; 10 | 11 | public class Application extends Controller { 12 | 13 | @Inject MyService s; 14 | 15 | public static Result index() { 16 | return ok(index.render(s.demonstrate())); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /inject/sample/app/Global.java: -------------------------------------------------------------------------------- 1 | import play.*; 2 | import play.libs.*; 3 | 4 | import java.util.*; 5 | 6 | import service.MyService; 7 | import com.typesafe.plugin.inject.InjectPlugin; 8 | 9 | public class Global extends GlobalSettings { 10 | 11 | public void onStart(Application app) { 12 | Logger.warn("getting an instance from guice:"+ app.plugin(InjectPlugin.class).getInstance(MyService.class)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/Global.java: -------------------------------------------------------------------------------- 1 | import play.*; 2 | import play.libs.*; 3 | 4 | import java.util.*; 5 | 6 | import service.MyService; 7 | import com.typesafe.plugin.inject.InjectPlugin; 8 | 9 | public class Global extends GlobalSettings { 10 | 11 | public void onStart(Application app) { 12 | Logger.warn("getting an instance from guice:"+ app.plugin(InjectPlugin.class).getInstance(MyService.class)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sbtgoodies/sample/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // SbtGoodies plugin 8 | addSbtPlugin("com.typesafe" % "play-plugins-sbtgoodies" % "0.2") 9 | 10 | // Use the Play sbt plugin for Play projects 11 | addSbtPlugin("play" % "sbt-plugin" % "2.1-08072012") 12 | -------------------------------------------------------------------------------- /redis/src/main/scala/com/typesafe/play/redis/SedisPoolProvider.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.play.redis 2 | 3 | import javax.inject.{Inject, Provider, Singleton} 4 | 5 | import org.sedis.Pool 6 | import redis.clients.jedis.JedisPool 7 | 8 | @Singleton 9 | class SedisPoolProvider @Inject()(jedisPool: JedisPool) extends Provider[Pool] { 10 | lazy val get: Pool = { 11 | val sedisPool = { 12 | new Pool(jedisPool) 13 | } 14 | sedisPool 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dust/sample/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # frame page 6 | GET / controllers.Application.frame() 7 | 8 | # JSON data 9 | GET /data controllers.Application.data() 10 | 11 | # Map static resources from the /public folder to the /assets URL path 12 | GET /assets/*file controllers.Assets.at(path="/public", file) 13 | 14 | -------------------------------------------------------------------------------- /dust/sample/project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import PlayProject._ 4 | 5 | object ApplicationBuild extends Build { 6 | 7 | val appName = "play-plugins-dust-sample" 8 | val appVersion = "1.0-SNAPSHOT" 9 | 10 | val appDependencies = Seq( 11 | // Add your project dependencies here, 12 | ) 13 | 14 | val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( 15 | 16 | ) 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /sbtgoodies/sample/project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import PlayProject._ 4 | 5 | object ApplicationBuild extends Build { 6 | 7 | val appName = "play-plugins-sbtgoodies-sample" 8 | val appVersion = "1.0-SNAPSHOT" 9 | 10 | val appDependencies = Seq( 11 | // Add your project dependencies here, 12 | ) 13 | 14 | val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( 15 | 16 | ) 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/controllers/Application.java: -------------------------------------------------------------------------------- 1 | package controllers; 2 | 3 | import play.*; 4 | import play.mvc.*; 5 | 6 | import views.html.*; 7 | 8 | import javax.inject.*; 9 | import service.*; 10 | 11 | public class Application extends Controller { 12 | 13 | private MyService s; 14 | 15 | @Inject public Application( MyService s) { 16 | this.s=s; 17 | } 18 | 19 | public Result index() { 20 | return ok(index.render(s.demonstrate())); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /dust/src/main/scala/com/typesafe/plugin/DustKeys.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin 2 | 3 | import sbt._ 4 | 5 | trait DustKeys { 6 | lazy val dustFileRegexFrom = SettingKey[String]("play-dust-file-regex-from") 7 | lazy val dustFileRegexTo = SettingKey[String]("play-dust-file-regex-to") 8 | lazy val dustAssetsGlob = SettingKey[PathFinder]("play-dust-assets-glob") 9 | lazy val dustAssetsDir = SettingKey[File]("play-dust-assets-dir") 10 | lazy val dustFileEnding = SettingKey[String]("play-dust-file-ending") 11 | } -------------------------------------------------------------------------------- /inject/sample/project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import PlayProject._ 4 | 5 | object ApplicationBuild extends Build { 6 | 7 | val appName = "j" 8 | val appVersion = "1.0-SNAPSHOT" 9 | 10 | val appDependencies = Seq( 11 | "com.typesafe" % "play-plugins-inject" % "2.0.2" 12 | 13 | ) 14 | 15 | val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( 16 | // Add your own project settings here 17 | ) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /dust/sample/app/controllers/Application.java: -------------------------------------------------------------------------------- 1 | package controllers; 2 | 3 | import org.codehaus.jackson.node.ObjectNode; 4 | 5 | import play.*; 6 | import play.mvc.*; 7 | import play.libs.*; 8 | import views.html.*; 9 | 10 | public class Application extends Controller { 11 | 12 | public static Result frame() { 13 | return ok(index.render()); 14 | } 15 | 16 | public static Result data() { 17 | ObjectNode json = Json.newObject(); 18 | 19 | json.put("name", "Json"); 20 | json.put("count", 1); 21 | 22 | return ok(json); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /inject/sample_without_static_field/project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import PlayProject._ 4 | 5 | object ApplicationBuild extends Build { 6 | 7 | val appName = "j" 8 | val appVersion = "1.0-SNAPSHOT" 9 | 10 | val appDependencies = Seq( 11 | "com.typesafe" % "play-plugins-inject" % "2.0.2" 12 | 13 | ) 14 | 15 | val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( 16 | // Add your own project settings here 17 | ) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk6 3 | - oraclejdk8 4 | language: scala 5 | env: 6 | - PROJECT=statsd 7 | - PROJECT=redis 8 | script: ./run-tests-travis.sh 9 | matrix: 10 | exclude: 11 | - jdk: openjdk6 12 | env: PROJECT=redis 13 | - jdk: oraclejdk8 14 | env: PROJECT=statsd 15 | services: 16 | - redis-server 17 | before_cache: 18 | - find $HOME/.ivy2 -name "ivydata-*.properties" -delete 19 | - find $HOME/.sbt -name "*.lock" -delete 20 | cache: 21 | directories: 22 | - $HOME/.ivy2/cache 23 | - $HOME/.sbt/boot/ 24 | sudo: false 25 | -------------------------------------------------------------------------------- /dust/sample/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // The Typesafe SNAPSHOT repository 8 | resolvers += Resolver.url("Typesafe Ivy Snapshots Repository", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns) 9 | 10 | // The Dust plugin 11 | addSbtPlugin("com.typesafe" % "play-plugins-dust" % "1.4.1") 12 | 13 | // Use the Play sbt plugin for Play projects 14 | -------------------------------------------------------------------------------- /inject/sample/app/module/Dependencies.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import com.google.inject.Provides; 4 | import javax.inject.Singleton; 5 | import service.*; 6 | 7 | public class Dependencies { 8 | 9 | @Provides 10 | @Singleton 11 | public Something makeSomething() { 12 | return new Something() { 13 | public String noWhat() { 14 | return "yay"; 15 | } 16 | }; 17 | } 18 | 19 | @Provides 20 | @Singleton 21 | public MyService makeService(Something s) { 22 | return new MyService(s) { 23 | public String demonstrate() { return s.noWhat();} 24 | }; 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /dust/src/main/scala/com/typesafe/plugin/DustPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin 2 | 3 | import sbt._ 4 | import Keys._ 5 | import org.apache.commons.io.FilenameUtils 6 | 7 | object DustPlugin extends Plugin with DustTasks { 8 | 9 | override def settings: Seq[Setting[_]] = super.settings ++ Seq( 10 | dustAssetsDir <<= (sourceDirectory in Compile)(src => (src / "assets")), 11 | dustFileEnding := ".tl", 12 | dustAssetsGlob <<= (dustAssetsDir)(assetsDir => assetsDir ** "*.tl"), 13 | dustFileRegexFrom <<= (dustFileEnding)(fileEnding => fileEnding), 14 | dustFileRegexTo <<= (dustFileEnding)(fileEnding => FilenameUtils.removeExtension(fileEnding) + ".js"), 15 | resourceGenerators in Compile <+= DustCompiler) 16 | 17 | } -------------------------------------------------------------------------------- /dust/src/test/scala/com/typesafe/plugin/DustTasksTest.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import com.typesafe.plugin._ 3 | 4 | class HelloWorldSpec extends Specification with DustTasks { 5 | "The Dust compiler" should { 6 | "resolve template names correctly on Unix" in { 7 | val file = "/var/projects/sample/app/assets/templates/foo.tl" 8 | val assetsDir = "/var/projects/sample/app/assets" 9 | templateName(file, assetsDir) must be_==("templates/foo") 10 | } 11 | "resolve template names correctly on Windows" in { 12 | val file = "C:\\projects\\sample\\app\\assets\\templates\\foo.tl" 13 | val assetsDir = "C:\\projects\\sample\\app\\assets" 14 | templateName(file, assetsDir) must be_==("templates/foo") 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /dust/sample/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 | 3 | 4 | @import play._ 5 | 6 | 7 | 8 | Test 9 | 10 | 11 | 12 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /statsd/src/main/scala/play/modules/statsd/api/Sugar.scala: -------------------------------------------------------------------------------- 1 | package play.modules.statsd.api 2 | import play.api.Play 3 | import play.api.Application 4 | 5 | /** 6 | * Sugar to make using Java API for Play nicer. 7 | */ 8 | object please { 9 | private[api] def config(name: String): String = { 10 | Play.current.configuration.getString(name) getOrElse { 11 | throw new IllegalStateException("[%s] prop is null".format(name)) 12 | } 13 | } 14 | 15 | private[api] def booleanConfig(name: String): Boolean = { 16 | // Use maybe application, so when we check if statsd is enabled, if there's no current application, no worries 17 | Play.maybeApplication.flatMap { _.configuration.getBoolean(name) } getOrElse false 18 | } 19 | 20 | private[api] def intConfig(name: String): Int = { 21 | Integer.parseInt(config(name)) 22 | } 23 | } -------------------------------------------------------------------------------- /util/build.sbt: -------------------------------------------------------------------------------- 1 | name := "play-plugins-util" 2 | 3 | organization := "com.typesafe.play.plugins" 4 | 5 | version := "2.3.1" 6 | 7 | scalaVersion := "2.11.1" 8 | 9 | crossScalaVersions := Seq("2.11.1", "2.10.4") 10 | 11 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 12 | 13 | libraryDependencies ++= Seq( 14 | "com.typesafe.play" %% "play" % "2.3.1" % "provided" 15 | ) 16 | 17 | publishTo <<= (version) { version: String => 18 | val nexus = "https://private-repo.typesafe.com/typesafe/" 19 | if (version.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "maven-snapshots/") 20 | else Some("releases" at nexus + "maven-releases/") 21 | } 22 | 23 | javacOptions ++= Seq("-source", "1.6", "-target", "1.6", "-Xlint:unchecked") 24 | 25 | scalacOptions += "-deprecation" 26 | -------------------------------------------------------------------------------- /statsd/sample/sample-statsd/conf/routes: -------------------------------------------------------------------------------- 1 | 2 | GET / controllers.Application.index 3 | GET /foo/bar controllers.Application.index 4 | GET /single/end/:param controllers.Application.singleParam(param) 5 | GET /single/middle/:param/f controllers.Application.singleParam(param) 6 | GET /regex/$param<[0-9]+> controllers.Application.singleParam(param) 7 | GET /rest/*param controllers.Application.singleParam(param) 8 | GET /multiple/:param1/:param2 controllers.Application.twoParams(param1, param2) 9 | # @statsd.key custom.key 10 | GET /key/in/comments controllers.Application.index 11 | GET /async controllers.Application.async 12 | GET /sync/failure controllers.Application.syncFailure 13 | GET /async/failure controllers.Application.asyncFailure 14 | GET /error controllers.Application.error -------------------------------------------------------------------------------- /sbtgoodies/build.sbt: -------------------------------------------------------------------------------- 1 | import sbt.Defaults._ 2 | 3 | sbtPlugin := true 4 | 5 | name := "play-plugins-sbtgoodies" 6 | 7 | version := "0.2" 8 | 9 | organization := "com.typesafe" 10 | 11 | addSbtPlugin("play" % "sbt-plugin" % "2.1-08072012") 12 | 13 | libraryDependencies += "com.sun.jna" % "jna" % "3.0.9" 14 | 15 | publishMavenStyle := false 16 | 17 | resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 18 | 19 | publishTo <<= (version) { version: String => 20 | val typesafeIvyReleases = Resolver.url("Typesafe Ivy Releases Repository", url("http://repo.typesafe.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns) 21 | val typesafeIvySnapshot = Resolver.url("Typesafe Ivy Snapshots Repository", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns) 22 | val repo = if (version.trim.endsWith("SNAPSHOT")) typesafeIvySnapshot 23 | else typesafeIvyReleases 24 | Some(repo) 25 | } 26 | -------------------------------------------------------------------------------- /inject/sample_without_static_field/app/module/Dependencies.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import com.google.inject.Provides; 4 | import javax.inject.Singleton; 5 | import service.*; 6 | import play.Play; 7 | import com.typesafe.plugin.inject.InjectPlugin; 8 | 9 | public class Dependencies { 10 | 11 | public static InjectPlugin inject() { 12 | return Play.application().plugin(InjectPlugin.class); 13 | } 14 | 15 | public static controllers.Application application() { 16 | return inject().getInstance(controllers.Application.class); 17 | } 18 | 19 | @Provides 20 | @Singleton 21 | public Something makeSomething() { 22 | return new Something() { 23 | public String noWhat() { 24 | return "yay"; 25 | } 26 | }; 27 | } 28 | 29 | @Provides 30 | @Singleton 31 | public MyService makeService(Something s) { 32 | return new MyService(s) { 33 | public String demonstrate() { return s.noWhat();} 34 | }; 35 | } 36 | 37 | 38 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unsupported Play Plugins 2 | 3 | These plugins are not supported by Typesafe and no guarantee is made that they will be maintained or upgraded to new versions of Play in a timely manner. 4 | 5 | Use at your own risk. 6 | 7 | ![Travis build status](https://travis-ci.org/typesafehub/play-plugins.svg?branch=master) 8 | 9 | ## Licence 10 | 11 | This software is licensed under the Apache 2 license, quoted below. 12 | 13 | Copyright 2012 Typesafe (http://www.typesafe.com). 14 | 15 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 16 | 17 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 18 | -------------------------------------------------------------------------------- /statsd/src/main/java/play/modules/statsd/StatsdFilter.java: -------------------------------------------------------------------------------- 1 | package play.modules.statsd; 2 | 3 | import play.api.mvc.EssentialAction; 4 | import play.api.mvc.EssentialFilter; 5 | 6 | /** 7 | * Filter for reporting request metrics to Statsd. 8 | * 9 | * Usage: 10 | * 11 | *
12 |  *     public class Global extends GlobalSettings {
13 |  *         public  Class[] filters() {
14 |  *             return new Class[] {StatsdFilter.class};
15 |  *         }
16 |  *     }
17 |  * 
18 | */ 19 | public class StatsdFilter implements EssentialFilter { 20 | 21 | // We have to keep a static reference since the Java GlobalSettings instantiates a new filter for every request, 22 | // and the filter holds a cache. 23 | private static final play.modules.statsd.api.StatsdFilter filter = new play.modules.statsd.api.StatsdFilter(); 24 | 25 | @Override 26 | public EssentialAction apply(EssentialAction next) { 27 | return filter.apply(next); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /statsd/sample/sample-statsd/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc._ 4 | import play.api.libs.concurrent.Execution.Implicits._ 5 | import scala.concurrent.Future 6 | 7 | object Application extends Controller { 8 | def index = Action { 9 | Thread.sleep(2) 10 | Ok 11 | } 12 | 13 | def singleParam(p: String) = Action { 14 | Thread.sleep(2) 15 | Ok 16 | } 17 | 18 | def twoParams(p1: String, p2: String) = Action { 19 | Thread.sleep(2) 20 | Ok 21 | } 22 | 23 | def async = Action.async { 24 | Future { 25 | Thread.sleep(2) 26 | Ok 27 | } 28 | } 29 | 30 | def syncFailure = Action { 31 | Thread.sleep(2) 32 | if (true) throw new RuntimeException 33 | Ok 34 | } 35 | 36 | def asyncFailure = Action.async { 37 | Future { 38 | Thread.sleep(2) 39 | if (true) throw new RuntimeException 40 | Ok 41 | } 42 | } 43 | 44 | def error = Action { 45 | Thread.sleep(2) 46 | ServiceUnavailable 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sbtlogger/build.sbt: -------------------------------------------------------------------------------- 1 | import sbt.Defaults._ 2 | 3 | sbtPlugin := true 4 | 5 | name := "play-plugins-logger" 6 | 7 | version := "0.2" 8 | 9 | organization := "com.typesafe" 10 | 11 | libraryDependencies <++= (scalaVersion, sbtVersion) { 12 | case (scalaVersion, sbtVersion) => Seq( 13 | sbtPluginExtra("play" % "sbt-plugin" % "2.0.2", sbtVersion, scalaVersion) 14 | ) 15 | } 16 | 17 | libraryDependencies += "com.sun.jna" % "jna" % "3.0.9" 18 | 19 | publishMavenStyle := false 20 | 21 | resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 22 | 23 | publishTo <<= (version) { version: String => 24 | val typesafeIvyReleases = Resolver.url("Typesafe Ivy Releases Repository", url("http://repo.typesafe.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns) 25 | val typesafeIvySnapshot = Resolver.url("Typesafe Ivy Snapshots Repository", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns) 26 | val repo = if (version.trim.endsWith("SNAPSHOT")) typesafeIvySnapshot 27 | else typesafeIvyReleases 28 | Some(repo) 29 | } 30 | -------------------------------------------------------------------------------- /inject/src/main/java/com/typesafe/plugin/inject/ManualInjectionPlugin.java: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin.inject; 2 | import play.*; 3 | 4 | public class ManualInjectionPlugin extends InjectPlugin { 5 | 6 | public ManualInjectionPlugin(Application app) { 7 | super(app); 8 | } 9 | 10 | public T getInstance(Class type) { 11 | return com.google.inject.mini.MiniGuice.inject(type, true, availableModules().toArray()); 12 | } 13 | 14 | @Override 15 | public boolean enabled() { 16 | return !(app.configuration().getString("manualinjectplugin") != null && app.configuration().getString("manualinjectplugin").equals("disabled") ); 17 | } 18 | 19 | @Override 20 | public void onStart() { 21 | //inject 22 | for (Class clazz : scanInjectableClasses() ) { 23 | try { 24 | Logger.debug("injection for "+ clazz.getName()); 25 | com.google.inject.mini.MiniGuice.inject(clazz, true, availableModules().toArray()); 26 | } catch (java.lang.IllegalArgumentException ex) { 27 | Logger.debug("skipping injection for "+ clazz.getName()); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /util/README.md: -------------------------------------------------------------------------------- 1 | # Utils 2 | 3 | small scala and java utilities that we are using across the board 4 | 5 | # Features 6 | 7 | * provides easy access to plugins, ```use[MyPlugin].myapimethod``` (Scala) 8 | 9 | * provides security implementation based on a request header and/or query parameter (Scala) 10 | 11 | # How to install 12 | (requires play 2.3.x) 13 | 14 | * add 15 | ```"com.typesafe.play.plugins" % "play-plugins-util" % "2.3.1"``` to your dependencies 16 | 17 | 18 | ## Licence 19 | 20 | This software is licensed under the Apache 2 license, quoted below. 21 | 22 | Copyright 2012 Typesafe (http://www.typesafe.com). 23 | 24 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 25 | 26 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 27 | -------------------------------------------------------------------------------- /dust/build.sbt: -------------------------------------------------------------------------------- 1 | import sbt.Defaults._ 2 | 3 | sbtPlugin := true 4 | 5 | name := "play-plugins-dust" 6 | 7 | version := "1.4.1-09122012" 8 | 9 | organization := "com.typesafe" 10 | 11 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 12 | 13 | libraryDependencies <++= (scalaVersion) { 14 | case (scalaVersion) => Seq( 15 | sbtPluginExtra("play" % "sbt-plugin" % "2.1-09092012" % "provided", "0.12", scalaVersion) 16 | ) 17 | } 18 | 19 | libraryDependencies += "commons-io" % "commons-io" % "2.2" 20 | 21 | libraryDependencies += "org.specs2" %% "specs2" % "1.12.3" % "test" 22 | 23 | publishMavenStyle := false 24 | 25 | publishTo <<= (version) { version: String => 26 | val typesafeIvyReleases = Resolver.url("Typesafe Ivy Releases Repository", url("http://typesafe.artifactoryonline.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns) 27 | val typesafeIvySnapshot = Resolver.url("Typesafe Ivy Snapshots Repository", url("http://typesafe.artifactoryonline.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns) 28 | val repo = if (version.trim.endsWith("SNAPSHOT")) typesafeIvySnapshot 29 | else typesafeIvyReleases 30 | Some(repo) 31 | } 32 | -------------------------------------------------------------------------------- /sbtlogger/README.md: -------------------------------------------------------------------------------- 1 | # SbtGoodies Plugin 2 | 3 | various sbt plugin addons 4 | 5 | # How to install 6 | 7 | * add 8 | (requires play 2.0.2) 9 | ``` 10 | addSbtPlugin("com.typesafe" % "play-plugins-sbtgoodies" % "0.2") 11 | ``` 12 | to your ```project/plugins.sbt``` 13 | 14 | and that's it! 15 | 16 | # How to Use 17 | 18 | available commands: 19 | 20 | * ```dist-unzip``` unzips a distribution, also adds execution bit (on 21 | *nix) or creates ```start.bat``` (on windows) 22 | 23 | # Sample 24 | 25 | For an example, see the bundled sample app 26 | 27 | ## Licence 28 | 29 | This software is licensed under the Apache 2 license, quoted below. 30 | 31 | Copyright 2012 Typesafe (http://www.typesafe.com). 32 | 33 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 34 | 35 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 36 | -------------------------------------------------------------------------------- /sbtlogger/src/main/scala/com/typesafe/util/Sbt.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.util 2 | 3 | import sbt._ 4 | 5 | object Sbt { 6 | def logger(extra: ScopedKey[_] => Seq[AbstractLogger]) = new TypesafeLogManager(extra) 7 | } 8 | 9 | class TypesafeLogManager(extra: ScopedKey[_] => Seq[AbstractLogger]) extends LogManager { 10 | 11 | val screen = LogManager.defaultScreen 12 | val backed = LogManager.defaultBacked() 13 | 14 | def apply(data: Settings[Scope], state: State, task: ScopedKey[_], to: java.io.PrintWriter): Logger = { 15 | new FilterLogger( 16 | delegate = LogManager.defaultLogger(data, state, task, screen, backed(to), extra(task).toList).asInstanceOf[AbstractLogger] 17 | ) { 18 | 19 | override def log(level: Level.Value, message: => String) { 20 | if(atLevel(level)) { 21 | if(filtered(message)) { 22 | print(".") 23 | } else { 24 | if (message.toLowerCase.contains("update") || message.toLowerCase.contains("updating")) println() 25 | super.log(level, message) 26 | } 27 | } 28 | } 29 | 30 | def filtered(message: String) = { 31 | message.startsWith("Resolving ") && message.endsWith("...") 32 | } 33 | 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /statsd/build.sbt: -------------------------------------------------------------------------------- 1 | name := "play-statsd" 2 | 3 | organization := "com.typesafe.play.plugins" 4 | 5 | version := "2.3.0" 6 | 7 | scalaVersion := "2.11.1" 8 | 9 | crossScalaVersions := Seq("2.11.1", "2.10.4") 10 | 11 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 12 | 13 | libraryDependencies ++= Seq( 14 | "com.typesafe.play" %% "play" % "2.3.0" % "provided", 15 | "com.typesafe.play" %% "play-test" % "2.3.0" % "test" 16 | ) 17 | 18 | parallelExecution in Test := false 19 | 20 | testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-v") 21 | 22 | publishTo <<= (version) { version: String => 23 | val nexus = "https://private-repo.typesafe.com/typesafe/" 24 | if (version.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "maven-snapshots/") 25 | else Some("releases" at nexus + "maven-releases/") 26 | } 27 | 28 | javacOptions ++= Seq("-source", "1.6", "-target", "1.6", "-Xlint:unchecked", "-encoding", "UTF-8") 29 | 30 | scalacOptions += "-deprecation" 31 | 32 | lazy val root = project in file(".") 33 | 34 | lazy val sample = (project in file("sample/sample-statsd")) 35 | .enablePlugins(PlayScala) 36 | .settings( 37 | Keys.fork in Test := false 38 | ).dependsOn(root).aggregate(root) 39 | -------------------------------------------------------------------------------- /inject/project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | object MinimalBuild extends Build { 5 | 6 | lazy val buildVersion = "2.0.3" 7 | lazy val playVersion = "2.0.2" 8 | 9 | lazy val typesafeSnapshot = "Typesafe Snapshots Repository" at "http://repo.typesafe.com/typesafe/snapshots/" 10 | lazy val typesafe = "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 11 | lazy val repo = if (buildVersion.endsWith("SNAPSHOT")) typesafeSnapshot else typesafe 12 | 13 | lazy val play = "play" %% "play" % playVersion 14 | 15 | 16 | lazy val root = Project(id = "play-plugins-inject", base = file("."), settings = Project.defaultSettings).settings( 17 | version := buildVersion, 18 | publishTo <<= (version) { version: String => 19 | val nexus = "http://repo.typesafe.com/typesafe/" 20 | if (version.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "ivy-snapshots/") 21 | else Some("releases" at nexus + "ivy-releases/") 22 | }, 23 | organization := "com.typesafe", 24 | resolvers += repo, 25 | javacOptions += "-Xlint:unchecked", 26 | libraryDependencies += play, 27 | crossPaths := false, 28 | libraryDependencies += "org.ow2.spec.ee" % "ow2-atinject-1.0-spec" % "1.0.10", 29 | libraryDependencies += "com.cedarsoft" % "guice-annotations" % "2.0.1" 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /util/src/main/scala/com/typesafe/plugin/package.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe 2 | 3 | package object plugin{ 4 | 5 | import play.api._ 6 | import play.api.mvc._ 7 | 8 | /** 9 | * provides easy access to plugins 10 | */ 11 | 12 | def use[A <: Plugin](implicit app: Application, m: Manifest[A]) = { 13 | app.plugin[A].getOrElse(throw new RuntimeException(m.runtimeClass.toString + " plugin should be available at this point")) 14 | } 15 | 16 | /** 17 | * provides a security implementation based on a request header and/or a token query parameter 18 | */ 19 | trait Secured { 20 | 21 | private def check(header: String): Option[String] = { 22 | val parts = header.split("-") 23 | if (parts.size != 2) None 24 | else { 25 | if (parts(0) == libs.Crypto.sign(parts(1))) Some(parts(1)) 26 | else None 27 | } 28 | } 29 | 30 | private def username(request: RequestHeader): Option[String] = { 31 | request.headers.get("X-Authenticated").map{header => 32 | check(header) 33 | }.getOrElse(request.queryString.get("token").map(param => check(param(0))).getOrElse(None)) 34 | 35 | } 36 | 37 | private def onUnauthorized(request: RequestHeader) = Results.Unauthorized 38 | 39 | // -- 40 | 41 | def isAuthenticated(f: => String => Request[AnyContent] => Result) = Security.Authenticated(username, onUnauthorized) { user => 42 | Action(request => f(user)(request)) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sbtgoodies/README.md: -------------------------------------------------------------------------------- 1 | # SbtGoodies Plugin 2 | 3 | various sbt plugin addons 4 | 5 | # How to install 6 | 7 | In your ```project/plugins.sbt```, add 8 | ``` 9 | addSbtPlugin("com.typesafe" % "play-plugins-sbtgoodies" % "0.1") 10 | ``` 11 | And then update your Build.scala to include the distUnzipSettings: 12 | 13 | import com.typesafe.plugin.SbtGoodiesPlugin 14 | 15 | val frontend = PlayProject( 16 | appName, 17 | appVersion, 18 | deps, 19 | file("frontend"), 20 | JAVA, 21 | Defaults.defaultSettings ++ SbtGoodiesPlugin.distUnzipSettings 22 | ) 23 | 24 | and that's it! 25 | 26 | # How to Use 27 | 28 | available commands: 29 | 30 | * ```dist-unzip``` unzips a distribution, also adds execution bit (on 31 | *nix) or creates ```start.bat``` (on windows) 32 | 33 | # Sample 34 | 35 | For an example, see the bundled sample app 36 | 37 | ## Licence 38 | 39 | This software is licensed under the Apache 2 license, quoted below. 40 | 41 | Copyright 2012 Typesafe (http://www.typesafe.com). 42 | 43 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 44 | 45 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 46 | -------------------------------------------------------------------------------- /inject/sample/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # If you deploy your application to several instances be sure to use the same key! 8 | application.secret="cBlQ[B0FM]DiFD 18 | val packageName = id + "-" + version 19 | val zip = distDir / (packageName + ".zip") 20 | val unzippedDir = distDir / packageName 21 | if (zip.exists) { 22 | IO.unzip(zip, distDir) 23 | val os = System.getProperty("os.name").toLowerCase() 24 | 25 | if (os.indexOf("win") <= 0) { 26 | //nix 27 | val libc = Native.loadLibrary("c", classOf[CLibrary]).asInstanceOf[CLibrary] 28 | libc.chmod(unzippedDir.getAbsolutePath+"/start", 0755) 29 | } else { 30 | //win 31 | val config = Option(System.getProperty("config.file")) 32 | val startbat = distDir / "start.bat" 33 | IO.write(startbat, 34 | """|@echo off 35 | |setlocal 36 | |set p=%~dp0 37 | |set p=%p:\=/% 38 | |java %* -cp "%p%lib/*" """ + config.map(fn => "-Dconfig.file=\"%p%" + fn + "\" ").getOrElse("") + """play.core.server.NettyServer "%p%" 39 | |""".stripMargin) 40 | } 41 | IO.delete(zip) 42 | s.log.info("Done!") 43 | s.log.info("Your unzipped distribution is ready in ") 44 | s.log.info(unzippedDir.getAbsolutePath) 45 | } else s.log.warn("could not find dist archive:"+zip.getAbsolutePath) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /dust/sample/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # If you deploy your application to several instances be sure to use the same key! 8 | application.secret="X:bU;HeE>lcO>h__p9FZ`MgklE3y/LJ]BHIuicLYHlPW2@4?XwR/qF]M6Hlp>aRe" 9 | 10 | # The application languages 11 | # ~~~~~ 12 | application.langs="en" 13 | 14 | # Global object class 15 | # ~~~~~ 16 | # Define the Global object class for this application. 17 | # Default to Global in the root package. 18 | # global=Global 19 | 20 | # Database configuration 21 | # ~~~~~ 22 | # You can declare as many datasources as you want. 23 | # By convention, the default datasource is named `default` 24 | # 25 | # db.default.driver=org.h2.Driver 26 | # db.default.url="jdbc:h2:mem:play" 27 | # db.default.user=sa 28 | # db.default.password= 29 | # 30 | # You can expose this datasource via JNDI if needed (Useful for JPA) 31 | # db.default.jndiName=DefaultDS 32 | 33 | # Evolutions 34 | # ~~~~~ 35 | # You can disable evolutions if needed 36 | # evolutionplugin=disabled 37 | 38 | # Ebean configuration 39 | # ~~~~~ 40 | # You can declare as many Ebean servers as you want. 41 | # By convention, the default server is named `default` 42 | # 43 | # ebean.default="models.*" 44 | 45 | # Logger 46 | # ~~~~~ 47 | # You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory . 48 | 49 | # Root logger: 50 | logger.root=ERROR 51 | 52 | # Logger used by the framework: 53 | logger.play=INFO 54 | 55 | # Logger provided to your application: 56 | logger.application=DEBUG 57 | 58 | # Static conf 59 | static.lib.root = "/Users/bguan/p/content/static/lib" 60 | static.js.root = "/Users/bguan/p/content/static/js" 61 | static.tl.root = "/Users/bguan/p/content/static/tl" 62 | app.root = "" -------------------------------------------------------------------------------- /sbtgoodies/sample/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # If you deploy your application to several instances be sure to use the same key! 8 | application.secret="X:bU;HeE>lcO>h__p9FZ`MgklE3y/LJ]BHIuicLYHlPW2@4?XwR/qF]M6Hlp>aRe" 9 | 10 | # The application languages 11 | # ~~~~~ 12 | application.langs="en" 13 | 14 | # Global object class 15 | # ~~~~~ 16 | # Define the Global object class for this application. 17 | # Default to Global in the root package. 18 | # global=Global 19 | 20 | # Database configuration 21 | # ~~~~~ 22 | # You can declare as many datasources as you want. 23 | # By convention, the default datasource is named `default` 24 | # 25 | # db.default.driver=org.h2.Driver 26 | # db.default.url="jdbc:h2:mem:play" 27 | # db.default.user=sa 28 | # db.default.password= 29 | # 30 | # You can expose this datasource via JNDI if needed (Useful for JPA) 31 | # db.default.jndiName=DefaultDS 32 | 33 | # Evolutions 34 | # ~~~~~ 35 | # You can disable evolutions if needed 36 | # evolutionplugin=disabled 37 | 38 | # Ebean configuration 39 | # ~~~~~ 40 | # You can declare as many Ebean servers as you want. 41 | # By convention, the default server is named `default` 42 | # 43 | # ebean.default="models.*" 44 | 45 | # Logger 46 | # ~~~~~ 47 | # You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory . 48 | 49 | # Root logger: 50 | logger.root=ERROR 51 | 52 | # Logger used by the framework: 53 | logger.play=INFO 54 | 55 | # Logger provided to your application: 56 | logger.application=DEBUG 57 | 58 | # Static conf 59 | static.lib.root = "/Users/bguan/p/content/static/lib" 60 | static.js.root = "/Users/bguan/p/content/static/js" 61 | static.tl.root = "/Users/bguan/p/content/static/tl" 62 | app.root = "" -------------------------------------------------------------------------------- /redis/build.sbt: -------------------------------------------------------------------------------- 1 | name := "play-modules-redis" 2 | organization := "com.typesafe.play.modules" 3 | 4 | crossScalaVersions := Seq("2.11.8") 5 | scalaVersion := "2.11.8" 6 | 7 | javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-Xlint:unchecked", "-encoding", "UTF-8") 8 | scalacOptions += "-deprecation" 9 | 10 | libraryDependencies ++= Seq( 11 | "com.typesafe.play" %% "play" % "2.5.3" % "provided", 12 | "com.typesafe.play" %% "play-test" % "2.5.3" % "test", 13 | "com.typesafe.play" %% "play-specs2" % "2.5.3" % "test", 14 | "com.typesafe.play" %% "play-cache" % "2.5.3", 15 | "biz.source_code" % "base64coder" % "2010-12-19", 16 | "org.sedis" %% "sedis" % "1.2.2" 17 | ) 18 | 19 | resolvers ++= Seq( 20 | "pk11 repo" at "http://pk11-scratch.googlecode.com/svn/trunk", 21 | "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases" 22 | ) 23 | 24 | pomExtra := { 25 | 26 | https://github.com/typesafehub/play-plugins 27 | scm:git:git@github.com:typesafehub/play-plugins.git 28 | 29 | 30 | 31 | typesafe 32 | Typesafe 33 | https://typesafe.com 34 | 35 | 36 | } 37 | pomIncludeRepository := { _ => false } 38 | homepage := Some(url(s"https://github.com/typesafehub/play-plugins")) 39 | licenses := Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) 40 | 41 | sonatypeProfileName := "com.typesafe" 42 | releasePublishArtifactsAction := PgpKeys.publishSigned.value 43 | releaseTagName := s"redis-${(version in ThisBuild).value}" 44 | releaseCrossBuild := true 45 | 46 | import ReleaseTransformations._ 47 | releaseProcess := Seq[ReleaseStep]( 48 | checkSnapshotDependencies, 49 | inquireVersions, 50 | runTest, 51 | setReleaseVersion, 52 | commitReleaseVersion, 53 | tagRelease, 54 | publishArtifacts, 55 | releaseStepCommand("sonatypeRelease"), 56 | setNextVersion, 57 | commitNextVersion, 58 | pushChanges 59 | ) 60 | -------------------------------------------------------------------------------- /dust/README.md: -------------------------------------------------------------------------------- 1 | # Dust Plugin 2 | 3 | An updated version of this plugin can be found here: [https://github.com/jmparsons/play-dustjs](https://github.com/jmparsons/play-dustjs). 4 | 5 | This plugin provides build time compilation for [Dust](https://github.com/akdubya/dustjs) templates. 6 | 7 | # How to install 8 | 9 | * add 10 | 11 | play 2.0.2: 12 | 13 | ```addSbtPlugin("com.typesafe" % "play-plugins-dust" % "1.4")``` 14 | 15 | play 2.0.1: 16 | 17 | ```addSbtPlugin("com.typesafe" % "play-plugins-dust" % "1.0-SNAPSHOT")``` 18 | 19 | to your plugin.sbt 20 | 21 | # How to Use 22 | 23 | * Include dust. Note that this is not provided by the sbt plugin. It can be found here: [dust-core-0.6.0.min.js](https://raw.github.com/typesafehub/play-plugins/master/dust/sample/public/javascripts/dust-core-0.6.0.min.js) 24 | ``` 25 | ``` 26 | 27 | * Put your dust template .tl files under the ```app/assets``` directory 28 | 29 | * Reference the generated .js in a `````` 31 | 32 | * Render the template when you receive the json 33 | ``` 34 | $(function() { 35 | $.get('@routes.Application.data', function(data) { 36 | console.log('data = ' + JSON.stringify(data)); 37 | dust.render('example.tl', data, function(err, out) { 38 | $('#dust_pan').html(err ? err : out); 39 | }); 40 | }); 41 | }); 42 | ``` 43 | 44 | 45 | # Sample 46 | 47 | For an example, see the bundled sample app 48 | 49 | ## Licence 50 | 51 | This software is licensed under the Apache 2 license, quoted below. 52 | 53 | Copyright 2012 Typesafe (http://www.typesafe.com). 54 | 55 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 56 | 57 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 58 | -------------------------------------------------------------------------------- /guice/README.md: -------------------------------------------------------------------------------- 1 | # Play 2.1 2 | this plugin now is obsolate. Use ```play.GlobalSetting#getControllerInstance``` instead. 3 | 4 | 5 | # For older verions of Play 6 | 7 | # Guice Plugin 8 | 9 | This plugin provides support for [Guice](http://code.google.com/p/google-guice/) 10 | 11 | # Features 12 | 13 | * Allow constructor injection via delegate 14 | 15 | this design pattern can be achieved the same as with manual Injection. 16 | [This] (https://github.com/typesafehub/play-plugins/tree/master/inject/sample_without_static_field) sample app demonstrates it. 17 | 18 | * Allows static field injection to a preconfigured package (```controllers``` by default) ie 19 | 20 | ```java 21 | //requires default constructor 22 | public class Application extends Controller { 23 | 24 | @Inject static Service s; 25 | 26 | public static Result index() { 27 | return ok(index.render(s.demonstrate())); 28 | } 29 | 30 | } 31 | ``` 32 | 33 | * Dependency modules are configurable (by default it's ```module.Dependencies```) 34 | 35 | * Allows direct access to the factory method ie 36 | 37 | ```java 38 | //in Global.java 39 | play.Play.application().plugin(InjectPlugin.class).getInstance(MyServiceInterface.class) 40 | ``` 41 | 42 | # How to install 43 | 44 | * add 45 | 46 | play 2.0.2: 47 | 48 | ```"com.typesafe" % "play-plugins-guice" % "2.0.3"``` to your dependencies 49 | 50 | play 2.0.1: 51 | 52 | ```"com.typesafe" % "play-plugins-guice" % "2.0.2"``` to your dependencies 53 | 54 | 55 | * create a file called ```play.plugins``` in your ```app/conf``` directory 56 | 57 | * add ```1500:com.typesafe.plugin.inject.GuicePlugin``` 58 | 59 | * that's it 60 | 61 | # Testing 62 | 63 | * testing can be achieved two ways in case of using field injection: 64 | 65 | * making the injected dependencies anything but private 66 | 67 | * injected dependencies can be private in which case a new constructor (alongside with an empty default one) could be added to controllers which could be used to inject the mocked dependencies 68 | (I would recommend the former solution) 69 | 70 | * testing is trivial in case of using constructor injection. 71 | 72 | # Sample 73 | 74 | for an example, see the bundled sample app 75 | 76 | 77 | ## Licence 78 | 79 | This software is licensed under the Apache 2 license, quoted below. 80 | 81 | Copyright 2012 Typesafe (http://www.typesafe.com). 82 | 83 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 84 | 85 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 86 | -------------------------------------------------------------------------------- /redis/src/main/scala/com/typesafe/play/redis/JedisPoolProvider.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.play.redis 2 | 3 | import java.net.URI 4 | import javax.inject.{Provider, Inject, Singleton} 5 | 6 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder 7 | import play.api.inject.ApplicationLifecycle 8 | import play.api.{Logger, Configuration} 9 | import redis.clients.jedis.{JedisPool, JedisPoolConfig} 10 | 11 | import scala.concurrent.Future 12 | 13 | @Singleton 14 | class JedisPoolProvider @Inject()(config: Configuration, lifecycle: ApplicationLifecycle) extends Provider[JedisPool]{ 15 | 16 | lazy val logger = Logger("redis.module") 17 | lazy val get: JedisPool = { 18 | val jedisPool = { 19 | val redisUri = config.getString("redis.uri").map(new URI(_)) 20 | 21 | val host = config.getString("redis.host") 22 | .orElse(redisUri.map(_.getHost)) 23 | .getOrElse("localhost") 24 | 25 | val port = config.getInt("redis.port") 26 | .orElse(redisUri.map(_.getPort).filter(_ != -1)) 27 | .getOrElse(6379) 28 | 29 | val password = config.getString("redis.password") 30 | .orElse(redisUri.map(_.getUserInfo).filter(_ != null).filter(_ contains ":").map(_.split(":", 2)(1))) 31 | .orNull 32 | 33 | val timeout = config.getInt("redis.timeout") 34 | .getOrElse(2000) 35 | 36 | val database = config.getInt("redis.database") 37 | .getOrElse(0) 38 | 39 | val poolConfig = createPoolConfig(config) 40 | Logger.info(s"Redis Plugin enabled. Connecting to Redis on $host:$port to $database with timeout $timeout.") 41 | Logger.info("Redis Plugin pool configuration: " + new ReflectionToStringBuilder(poolConfig).toString) 42 | 43 | 44 | new JedisPool(poolConfig, host, port, timeout, password, database) 45 | } 46 | 47 | logger.info("Starting Jedis Pool Provider") 48 | 49 | lifecycle.addStopHook(() => Future.successful { 50 | logger.info("Stopping Jedis Pool Provider") 51 | jedisPool.destroy() 52 | }) 53 | 54 | jedisPool 55 | } 56 | 57 | private def createPoolConfig(config: Configuration): JedisPoolConfig = { 58 | val poolConfig: JedisPoolConfig = new JedisPoolConfig() 59 | config.getInt("redis.pool.maxIdle").foreach(poolConfig.setMaxIdle) 60 | config.getInt("redis.pool.minIdle").foreach(poolConfig.setMinIdle) 61 | config.getInt("redis.pool.maxTotal").foreach(poolConfig.setMaxTotal) 62 | config.getLong("redis.pool.maxWaitMillis").foreach(poolConfig.setMaxWaitMillis) 63 | config.getBoolean("redis.pool.testOnBorrow").foreach(poolConfig.setTestOnBorrow) 64 | config.getBoolean("redis.pool.testOnReturn").foreach(poolConfig.setTestOnReturn) 65 | config.getBoolean("redis.pool.testWhileIdle").foreach(poolConfig.setTestWhileIdle) 66 | config.getLong("redis.pool.timeBetweenEvictionRunsMillis").foreach(poolConfig.setTimeBetweenEvictionRunsMillis) 67 | config.getInt("redis.pool.numTestsPerEvictionRun").foreach(poolConfig.setNumTestsPerEvictionRun) 68 | config.getLong("redis.pool.minEvictableIdleTimeMillis").foreach(poolConfig.setMinEvictableIdleTimeMillis) 69 | config.getLong("redis.pool.softMinEvictableIdleTimeMillis").foreach(poolConfig.setSoftMinEvictableIdleTimeMillis) 70 | config.getBoolean("redis.pool.lifo").foreach(poolConfig.setLifo) 71 | config.getBoolean("redis.pool.blockWhenExhausted").foreach(poolConfig.setBlockWhenExhausted) 72 | poolConfig 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /inject/src/main/java/com/typesafe/plugin/inject/InjectPlugin.java: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin.inject; 2 | 3 | import play.*; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | public abstract class InjectPlugin extends Plugin { 7 | 8 | protected Application app; 9 | 10 | private final String scannedPackage; 11 | 12 | private final String moduleNames; 13 | 14 | private final String filter; 15 | 16 | private ArrayList _modules = null; 17 | 18 | public InjectPlugin(Application app) { 19 | this.app = app; 20 | scannedPackage = app.configuration().getString("inject.package"); 21 | moduleNames = app.configuration().getString("inject.modules"); 22 | filter = app.configuration().getString("inject.filter"); 23 | } 24 | 25 | abstract public T getInstance(Class type); 26 | 27 | protected List availableModules() { 28 | if ( _modules == null) { 29 | _modules = createModules(); 30 | } 31 | return _modules; 32 | } 33 | 34 | /** 35 | * by default select only play.mvc.Controllers 36 | */ 37 | @SuppressWarnings(value = "unchecked") 38 | protected Class[] selectClasses(Class[] fullClassList) { 39 | ArrayList> classNames = new ArrayList>(); 40 | if (filter == null) { 41 | for (Class c: fullClassList) { 42 | if (play.mvc.Controller.class.isAssignableFrom(c)) { 43 | classNames.add(c); 44 | continue; 45 | } 46 | if (play.api.mvc.Controller.class.isAssignableFrom(c)) { 47 | classNames.add(c); 48 | } 49 | } 50 | } else { 51 | try { 52 | Class clazz = app.classloader().loadClass(filter); 53 | for (Class c: fullClassList) { 54 | if (clazz.isAssignableFrom(c)) { 55 | classNames.add(c); 56 | } 57 | } 58 | } catch (Exception ex) { 59 | Logger.warn ("could not create "+ filter); 60 | ex.printStackTrace(); 61 | } 62 | } 63 | Class[] r = (Class[]) classNames.toArray(new Class[classNames.size()]); 64 | return (Class[])r; 65 | } 66 | 67 | 68 | protected Class[] scanInjectableClasses() { 69 | if (scannedPackage == null) 70 | return selectClasses(Helper.getClasses("controllers", app.classloader())); 71 | else 72 | return selectClasses(Helper.getClasses(scannedPackage, app.classloader())); 73 | } 74 | 75 | protected String[] moduleNames() { 76 | if (moduleNames == null) { 77 | String[] module = new String[1]; 78 | module[0] = "module.Dependencies"; 79 | return module; 80 | } else 81 | return moduleNames.split(","); 82 | } 83 | 84 | private ArrayList createModules() { 85 | ArrayList modules = new ArrayList(); 86 | try { 87 | for (String module : moduleNames()) { 88 | modules.add(app.classloader().loadClass(module).newInstance()); 89 | } 90 | } catch (ClassNotFoundException x) { 91 | Logger.warn("maybe inject.modules config parameter is not set propery?"); 92 | x.printStackTrace(); 93 | } catch (InstantiationException x) { 94 | Logger.warn("Modules:"+ app.configuration().getString("inject.modules")+" could not be ceated" ); 95 | x.printStackTrace(); 96 | } catch (IllegalAccessException x) { 97 | x.printStackTrace(); 98 | } 99 | return modules; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /inject/src/main/java/com/typesafe/plugin/inject/Helper.java: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin.inject; 2 | import java.util.*; 3 | import java.io.*; 4 | import java.net.*; 5 | import java.util.zip.*; 6 | 7 | public class Helper { 8 | /** 9 | * Scans all classes accessible from the context class loader which belong to the given package and subpackages. 10 | * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files 11 | * @param packageName The base package 12 | * @return The classes 13 | * @throws ClassNotFoundException 14 | * @throws IOException 15 | */ 16 | public static Class[] getClasses(String packageName, ClassLoader classLoader) { 17 | try { 18 | assert classLoader != null; 19 | String path = packageName.replace('.', '/'); 20 | Enumeration resources = classLoader.getResources(path); 21 | List dirs = new ArrayList(); 22 | while (resources.hasMoreElements()) { 23 | URL resource = resources.nextElement(); 24 | dirs.add(resource.getFile()); 25 | } 26 | TreeSet classes = new TreeSet(); 27 | for (String directory : dirs) { 28 | classes.addAll(findClasses(directory, packageName)); 29 | } 30 | ArrayList classList = new ArrayList(); 31 | for (String clazz : classes) { 32 | classList.add(classLoader.loadClass(clazz)); 33 | } 34 | return classList.toArray(new Class[classes.size()]); 35 | } 36 | catch (Exception e) { 37 | e.printStackTrace(); 38 | return null; 39 | } 40 | } 41 | 42 | /** 43 | * Recursive method used to find all classes in a given directory and subdirs. 44 | * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files 45 | * @param directory The base directory 46 | * @param packageName The package name for classes found inside the base directory 47 | * @return The classes 48 | * @throws ClassNotFoundException 49 | */ 50 | private static TreeSet findClasses(String directory, String packageName) throws Exception { 51 | TreeSet classes = new TreeSet(); 52 | if (directory.startsWith("file:") && directory.contains("!")) { 53 | String [] split = directory.split("!"); 54 | URL jar = new URL(split[0]); 55 | ZipInputStream zip = new ZipInputStream(jar.openStream()); 56 | ZipEntry entry = null; 57 | while ((entry = zip.getNextEntry()) != null) { 58 | if (entry.getName().endsWith(".class")) { 59 | String className = entry.getName().replaceAll("[$].*", "").replaceAll("[.]class", "").replace('/', '.'); 60 | if (className.startsWith(packageName)) classes.add(className); 61 | } 62 | } 63 | } 64 | File dir = new File(directory); 65 | if (!dir.exists()) { 66 | return classes; 67 | } 68 | File[] files = dir.listFiles(); 69 | for (File file : files) { 70 | if (file.isDirectory()) { 71 | assert !file.getName().contains("."); 72 | classes.addAll(findClasses(file.getAbsolutePath(), packageName + "." + file.getName())); 73 | } else if (file.getName().endsWith(".class")) { 74 | classes.add(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)); 75 | } 76 | } 77 | return classes; 78 | } 79 | } -------------------------------------------------------------------------------- /statsd/src/main/scala/play/modules/statsd/api/StatsdFilter.scala: -------------------------------------------------------------------------------- 1 | package play.modules.statsd.api 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | import play.api.libs.concurrent.Execution.Implicits._ 6 | import scala.collection.concurrent.TrieMap 7 | import java.util.Locale 8 | import util.control.NonFatal 9 | 10 | /** 11 | * Filter for reporting request metrics to statsd. Usage: 12 | * 13 | * {{{ 14 | * object Global extends WithFilters(new StatsdFilter()) { 15 | * ... 16 | * } 17 | * }}} 18 | */ 19 | class StatsdFilter extends EssentialFilter { 20 | 21 | lazy val prefix = loadPrefix("statsd.routes.prefix", "routes.") 22 | lazy val totalPrefix = loadPrefix("statsd.routes.combined.prefix", "routes.combined.") 23 | 24 | def loadPrefix(key: String, default: String) = (for { 25 | app <- Play.maybeApplication 26 | prefix <- app.configuration.getString(key) 27 | } yield { 28 | if (prefix.length() == 0) prefix else prefix + "." 29 | }).getOrElse(default) 30 | 31 | lazy val pathSeparator: Char = (for { 32 | app <- Play.maybeApplication 33 | pathSeparatorString <- app.configuration.getString("statsd.routes.pathSeparator") 34 | } yield { 35 | if (pathSeparatorString.length != 1) { 36 | throw new IllegalStateException("pathSeparator [%s] is not a character".format(pathSeparatorString)) 37 | } 38 | pathSeparatorString.charAt(0) 39 | }).getOrElse('.') 40 | 41 | def apply(next: EssentialAction) = new EssentialAction { 42 | def apply(rh: RequestHeader) = { 43 | 44 | val start = System.currentTimeMillis() 45 | 46 | // Calculate key 47 | val key = rh.tags.get(Routes.ROUTE_VERB).map({ verb => 48 | val path = rh.tags(Routes.ROUTE_PATTERN) 49 | val cacheKey = verb + path 50 | prefix + keyCache.get(cacheKey).getOrElse { 51 | val key = statsKeyFromComments(rh.tags(Routes.ROUTE_COMMENTS)).getOrElse { 52 | // Convert paths of form GET /foo/bar/$paramname/blah to foo.bar.paramname.blah.get 53 | val p = path.replaceAll("""\$([^<]+)<[^>]+>""", "$1").replace('/', pathSeparator).dropWhile(_ == pathSeparator) 54 | val normalisedPath = if (p.lastOption.filter(_ != '.').isDefined) p + '.' else p 55 | normalisedPath + verb.toLowerCase(Locale.ENGLISH) 56 | } 57 | keyCache.putIfAbsent(cacheKey, key) 58 | key 59 | } 60 | }).getOrElse(totalPrefix + "handlerNotFound") 61 | 62 | Statsd.increment(key) 63 | 64 | def handleError = { 65 | val time = System.currentTimeMillis() - start 66 | Statsd.timing(key, time) 67 | Statsd.timing(totalPrefix + "time", time) 68 | Statsd.increment(totalPrefix + "500") 69 | Statsd.increment(totalPrefix + "error") 70 | } 71 | 72 | def recordStats(result: Result) = { 73 | val time = System.currentTimeMillis() - start 74 | Statsd.timing(key, time) 75 | Statsd.timing(totalPrefix + "time", time) 76 | val status = result.header.status 77 | Statsd.increment(totalPrefix + status) 78 | if (status >= 500) { 79 | Statsd.increment(totalPrefix + "error") 80 | } else { 81 | Statsd.increment(totalPrefix + "success") 82 | } 83 | result 84 | } 85 | 86 | // Invoke the action 87 | try { 88 | next(rh).map(recordStats) 89 | } catch { 90 | case NonFatal(t) => { 91 | handleError 92 | throw t 93 | } 94 | } 95 | } 96 | } 97 | 98 | val StatsdKey = """.*@statsd\.key[ \t]+([^\s@]+).*""".r 99 | 100 | def statsKeyFromComments(comments: String): Option[String] = { 101 | comments match { 102 | case StatsdKey(key) => Some(key) 103 | case _ => None 104 | } 105 | } 106 | 107 | val keyCache = TrieMap.empty[String, String] 108 | 109 | } 110 | -------------------------------------------------------------------------------- /redis/src/main/scala/com/typesafe/play/redis/RedisModule.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.play.redis 2 | 3 | import javax.inject.{Inject, Provider} 4 | 5 | import org.sedis.Pool 6 | import play.api.cache.{CacheApi, Cached, NamedCache} 7 | import play.api.inject._ 8 | import play.api.{Configuration, Environment} 9 | import play.cache.{CacheApi => JavaCacheApi, DefaultCacheApi => DefaultJavaCacheApi, NamedCacheImpl} 10 | import redis.clients.jedis.JedisPool 11 | 12 | /** 13 | * Redis cache components for compile time injection 14 | */ 15 | trait RedisCacheComponents { 16 | def environment: Environment 17 | def configuration: Configuration 18 | def applicationLifecycle: ApplicationLifecycle 19 | 20 | lazy val jedisPool: JedisPool = new JedisPoolProvider(configuration, applicationLifecycle).get 21 | lazy val sedisPool: Pool = new SedisPoolProvider(jedisPool).get 22 | 23 | /** 24 | * Use this to create with the given name. 25 | */ 26 | def cacheApi(name: String): CacheApi = { 27 | new RedisCacheApi(name, sedisPool, environment.classLoader) 28 | } 29 | 30 | lazy val redisDefaultCacheApi: CacheApi = cacheApi(RedisModule.defaultCacheNameFromConfig(configuration)) 31 | } 32 | 33 | class RedisModule extends Module { 34 | 35 | import scala.collection.JavaConversions._ 36 | 37 | override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = { 38 | val ehcacheDisabled = configuration.getStringList("play.modules.disabled").fold(false)(x => x.contains("play.api.cache.EhCacheModule")) 39 | val defaultCacheName = RedisModule.defaultCacheNameFromConfig(configuration) 40 | val bindCaches = configuration.underlying.getStringList("play.cache.redis.bindCaches").toSeq 41 | 42 | // Creates a named cache qualifier 43 | def named(name: String): NamedCache = { 44 | new NamedCacheImpl(name) 45 | } 46 | 47 | // bind a cache with the given name 48 | def bindCache(name: String) = { 49 | val namedCache = named(name) 50 | val cacheApiKey = bind[CacheApi].qualifiedWith(namedCache) 51 | Seq( 52 | cacheApiKey.to(new NamedRedisCacheApiProvider(name, bind[Pool], environment.classLoader)), 53 | bind[JavaCacheApi].qualifiedWith(namedCache).to(new NamedJavaCacheApiProvider(cacheApiKey)), 54 | bind[Cached].qualifiedWith(namedCache).to(new NamedCachedProvider(cacheApiKey)) 55 | ) 56 | } 57 | 58 | val defaultBindings = Seq( 59 | bind[JedisPool].toProvider[JedisPoolProvider], 60 | bind[Pool].toProvider[SedisPoolProvider], 61 | bind[JavaCacheApi].to[DefaultJavaCacheApi] 62 | ) ++ bindCaches.flatMap(bindCache) 63 | 64 | // alias the default cache to the unqualified implementation only if the default cache is disabled as it already does this. 65 | if (ehcacheDisabled) 66 | Seq(bind[CacheApi].to(bind[CacheApi].qualifiedWith(named(defaultCacheName)))) ++ bindCache(defaultCacheName) ++ defaultBindings 67 | else 68 | defaultBindings 69 | } 70 | } 71 | 72 | object RedisModule { 73 | def defaultCacheNameFromConfig(configuration: Configuration): String = { 74 | configuration.underlying.getString("play.cache.defaultCache") 75 | } 76 | } 77 | 78 | class NamedRedisCacheApiProvider(namespace: String, client: BindingKey[Pool], classLoader: ClassLoader) extends Provider[CacheApi] { 79 | @Inject private var injector: Injector = _ 80 | lazy val get: CacheApi = { 81 | new RedisCacheApi(namespace, injector.instanceOf(client), classLoader) 82 | } 83 | } 84 | 85 | class NamedJavaCacheApiProvider(key: BindingKey[CacheApi]) extends Provider[JavaCacheApi] { 86 | @Inject private var injector: Injector = _ 87 | lazy val get: JavaCacheApi = { 88 | new DefaultJavaCacheApi(injector.instanceOf(key)) 89 | } 90 | } 91 | 92 | class NamedCachedProvider(key: BindingKey[CacheApi]) extends Provider[Cached] { 93 | @Inject private var injector: Injector = _ 94 | lazy val get: Cached = { 95 | new Cached(injector.instanceOf(key)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /statsd/src/test/java/play/modules/statsd/StatsdTest.java: -------------------------------------------------------------------------------- 1 | package play.modules.statsd; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import play.libs.F; 8 | import play.test.FakeApplication; 9 | import play.test.Helpers; 10 | 11 | import java.io.IOException; 12 | import java.net.DatagramPacket; 13 | import java.net.DatagramSocket; 14 | import java.net.SocketTimeoutException; 15 | import java.util.Map; 16 | 17 | import static org.hamcrest.CoreMatchers.equalTo; 18 | import static org.junit.Assert.assertThat; 19 | import static org.junit.Assert.fail; 20 | 21 | public class StatsdTest { 22 | private static final int PORT = 57475; 23 | private DatagramSocket mockStatsd; 24 | private FakeApplication fakeApp; 25 | 26 | @Before 27 | public void setUp() throws IOException { 28 | mockStatsd = new DatagramSocket(PORT); 29 | mockStatsd.setSoTimeout(200); 30 | Map config = ImmutableMap.builder().put("ehcacheplugin", "disabled") 31 | .put("statsd.enabled", "true") 32 | .put("statsd.host", "localhost") 33 | .put("statsd.port", Integer.toString(PORT)).build(); 34 | fakeApp = Helpers.fakeApplication(config); 35 | Helpers.start(fakeApp); 36 | } 37 | 38 | @After 39 | public void tearDown() throws Exception { 40 | Helpers.stop(fakeApp); 41 | mockStatsd.close(); 42 | } 43 | 44 | @Test 45 | public void gaugeShouldSendGaugeMessage() throws Exception { 46 | Statsd.gauge("test", 42); 47 | assertThat(receive(), equalTo("statsd.test:42|g")); 48 | } 49 | 50 | @Test 51 | public void gaugeWithDeltaShouldSendGaugeMessage() throws Exception { 52 | Statsd.gauge("test", 10, true); 53 | assertThat(receive(), equalTo("statsd.test:+10|g")); 54 | Statsd.gauge("test", -10, true); 55 | assertThat(receive(), equalTo("statsd.test:-10|g")); 56 | } 57 | 58 | @Test 59 | public void incrementShouldSendIncrementByOneMessage() throws Exception { 60 | Statsd.increment("test"); 61 | assertThat(receive(), equalTo("statsd.test:1|c")); 62 | } 63 | 64 | @Test 65 | public void incrementShouldSendIncrementByManyMessage() throws Exception { 66 | Statsd.increment("test", 10); 67 | assertThat(receive(), equalTo("statsd.test:10|c")); 68 | } 69 | 70 | @Test 71 | public void timingShouldSendTimingMessage() throws Exception { 72 | Statsd.timing("test", 1234); 73 | assertThat(receive(), equalTo("statsd.test:1234|ms")); 74 | } 75 | 76 | @Test 77 | public void incrementShouldHopefullySendMessageWhenSampleRateJustBelowOne() throws Exception { 78 | Statsd.increment("test", 0.999999); 79 | assertThat(receive(), equalTo("statsd.test:1|c|@0.999999")); 80 | } 81 | 82 | @Test 83 | public void functionShouldBeTimedAndReportMessage() throws Exception { 84 | String result = Statsd.time("test", new F.Function0() { 85 | @Override 86 | public String apply() throws Throwable { 87 | Thread.sleep(10); 88 | return "the result"; 89 | } 90 | }); 91 | assertThat(result, equalTo("the result")); 92 | String msg = receive(); 93 | assertThat(msg, msg.startsWith("statsd.test:"), equalTo(true)); 94 | assertThat(msg, msg.endsWith("|ms"), equalTo(true)); 95 | } 96 | 97 | private String receive() throws IOException { 98 | byte[] buf = new byte[1024]; 99 | DatagramPacket packet = new DatagramPacket(buf, buf.length); 100 | try { 101 | mockStatsd.receive(packet); 102 | } catch (SocketTimeoutException e) { 103 | fail("Message not received after 200ms"); 104 | } 105 | return new String(packet.getData(), 0, packet.getLength()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /statsd/src/main/java/play/modules/statsd/Statsd.java: -------------------------------------------------------------------------------- 1 | package play.modules.statsd; 2 | 3 | import play.libs.F; 4 | import play.modules.statsd.api.Statsd$; 5 | import play.modules.statsd.api.StatsdClient; 6 | import scala.runtime.AbstractFunction0; 7 | 8 | /** 9 | * Java API to Statsd 10 | */ 11 | public class Statsd { 12 | 13 | /** 14 | * Increment the given key by 1 15 | * 16 | * @param key The key to increment 17 | */ 18 | public static void increment(String key) { 19 | client().increment(key, 1, 1.0); 20 | } 21 | 22 | /** 23 | * Increment the given key by the given value 24 | * 25 | * @param key The key to increment 26 | * @param value The value to increment it by 27 | */ 28 | public static void increment(String key, long value) { 29 | client().increment(key, value, 1.0); 30 | } 31 | 32 | /** 33 | * Increment the given key by the given value at the given rate 34 | * 35 | * @param key The key to increment 36 | * @param value The value to increment it by 37 | * @param rate The rate to sample at 38 | */ 39 | public static void increment(String key, long value, double rate) { 40 | client().increment(key, value, rate); 41 | } 42 | 43 | /** 44 | * Increment the given key by 1 at the given rate 45 | * 46 | * @param key The key to increment 47 | * @param rate The rate to sample at 48 | */ 49 | public static void increment(String key, double rate) { 50 | client().increment(key, 1, rate); 51 | } 52 | 53 | /** 54 | * Reporting timing for the given key 55 | * 56 | * @param key The key to report timing for 57 | * @param ms The time to report 58 | */ 59 | public static void timing(String key, long ms) { 60 | client().timing(key, ms, 1.0); 61 | } 62 | 63 | /** 64 | * Reporting timing for the given key at the given rate 65 | * 66 | * @param key The key to report timing for 67 | * @param ms The time to report 68 | * @param rate The rate to sample at 69 | */ 70 | public static void timing(String key, long ms, double rate) { 71 | client().timing(key, ms, rate); 72 | } 73 | 74 | /** 75 | * Time the given function and report the timing on the given key 76 | * 77 | * @param key The key to report timing for 78 | * @param timed The function to time 79 | */ 80 | public static T time(String key, F.Function0 timed) { 81 | return time(key, 1.0, timed); 82 | } 83 | 84 | /** 85 | * Time the given function and report the timing on the given key at the given rate 86 | * 87 | * @param key The key to report timing for 88 | * @param rate The rate to sample at 89 | * @param timed The function to time 90 | */ 91 | public static T time(String key, double rate, final F.Function0 timed) { 92 | return client().time(key, rate, new AbstractFunction0() { 93 | public T apply() { 94 | try { 95 | return timed.apply(); 96 | } catch (RuntimeException e) { 97 | throw e; 98 | } catch (Throwable t) { 99 | throw new RuntimeException(t); 100 | } 101 | } 102 | }); 103 | } 104 | 105 | /** 106 | * Record the given value. 107 | * 108 | * @param key The stat key to update. 109 | * @param value The value to record for the stat. 110 | */ 111 | public static void gauge(String key, long value) { 112 | client().gauge(key, value); 113 | } 114 | 115 | /** 116 | * Record the given value. 117 | * 118 | * @param key The stat key to update. 119 | * @param value The value to record for the stat. 120 | */ 121 | public static void gauge(String key, long value, boolean delta) { 122 | client().gauge(key, value, delta); 123 | } 124 | 125 | private static StatsdClient client() { 126 | return Statsd$.MODULE$; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /dust/src/main/scala/com/typesafe/plugin/DustTasks.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.plugin 2 | 3 | import java.io.File 4 | import java.io.FileInputStream 5 | import java.io.InputStreamReader 6 | import org.apache.commons.io.FilenameUtils 7 | import org.mozilla.javascript.tools.shell.Global 8 | import org.mozilla.javascript.Context 9 | import org.mozilla.javascript.JavaScriptException 10 | import org.mozilla.javascript.Scriptable 11 | import org.mozilla.javascript.ScriptableObject 12 | 13 | import sbt._ 14 | import PlayProject._ 15 | 16 | trait DustTasks extends DustKeys { 17 | 18 | def compile(name: String, source: String): Either[(String, Int, Int), String] = { 19 | 20 | import org.mozilla.javascript._ 21 | import org.mozilla.javascript.tools.shell._ 22 | 23 | import com.typesafe.plugin.DustKeys; 24 | 25 | import scala.collection.JavaConversions._ 26 | 27 | import java.io._ 28 | 29 | val ctx = Context.enter 30 | val global = new Global; global.init(ctx) 31 | val scope = ctx.initStandardObjects(global) 32 | 33 | ctx.evaluateReader( 34 | scope, 35 | new InputStreamReader(this.getClass.getClassLoader.getResource("dust-full-0.6.0.js").openConnection().getInputStream()), 36 | "dust.js", 37 | 1, null) 38 | 39 | ScriptableObject.putProperty(scope, "rawSource", source) 40 | ScriptableObject.putProperty(scope, "name", name) 41 | 42 | try { 43 | Right(ctx.evaluateString(scope, "(dust.compile(rawSource, name))", "JDustCompiler", 0, null).toString) 44 | } catch { 45 | case e: JavaScriptException => { 46 | val jsError = e.getValue.asInstanceOf[Scriptable] 47 | val message = ScriptableObject.getProperty(jsError, "message").toString 48 | 49 | // dust.js has weird error reporting where the line/column are part of the message, so we have to use a Regex to find them 50 | val DustCompileError = ".* At line : (\\d+), column : (\\d+)".r 51 | 52 | message match { 53 | case DustCompileError(line, column) => Left(message, line.toInt, column.toInt) 54 | case _ => Left(message, 0, 0) // Some other weird error, we have no line/column info now. 55 | } 56 | } 57 | } 58 | } 59 | 60 | protected def templateName(sourceFile: String, assetsDir: String): String = { 61 | val sourceFileWithForwardSlashes = FilenameUtils.separatorsToUnix(sourceFile) 62 | val assetsDirWithForwardSlashes = FilenameUtils.separatorsToUnix(assetsDir) 63 | FilenameUtils.removeExtension( 64 | sourceFileWithForwardSlashes.replace(assetsDirWithForwardSlashes + "/", "") 65 | ) 66 | } 67 | 68 | import Keys._ 69 | 70 | lazy val DustCompiler = (sourceDirectory in Compile, resourceManaged in Compile, cacheDirectory, dustFileRegexFrom, dustFileRegexTo, dustAssetsDir, dustAssetsGlob) map { 71 | (src, resources, cache, fileReplaceRegexp, fileReplaceWith, assetsDir, files) => 72 | val cacheFile = cache / "dust" 73 | 74 | def naming(name: String) = name.replaceAll(fileReplaceRegexp, fileReplaceWith) 75 | 76 | val currentInfos = files.get.map(f => f -> FileInfo.lastModified(f)).toMap 77 | 78 | val (previousRelation, previousInfo) = Sync.readInfo(cacheFile)(FileInfo.lastModified.format) 79 | val previousGeneratedFiles = previousRelation._2s 80 | 81 | if (previousInfo != currentInfos) { 82 | 83 | previousGeneratedFiles.foreach(IO.delete) 84 | 85 | val generated = (files x relativeTo(assetsDir)).flatMap { 86 | case (sourceFile, name) => { 87 | val msg = compile(templateName(sourceFile.getPath, assetsDir.getPath), IO.read(sourceFile)).left.map { 88 | case (msg, line, column) => throw AssetCompilationException(Some(sourceFile), 89 | msg, 90 | line, 91 | column) 92 | }.right.get 93 | 94 | val out = new File(resources, "public/" + naming(name)) 95 | IO.write(out, msg) 96 | Seq(sourceFile -> out) 97 | } 98 | } 99 | 100 | Sync.writeInfo(cacheFile, 101 | Relation.empty[java.io.File, java.io.File] ++ generated, 102 | currentInfos)(FileInfo.lastModified.format) 103 | 104 | generated.map(_._2).distinct.toList 105 | } else { 106 | previousGeneratedFiles.toSeq 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /statsd/src/main/scala/play/modules/statsd/api/StatsdClientCake.scala: -------------------------------------------------------------------------------- 1 | package play.modules.statsd.api 2 | 3 | import java.net.DatagramPacket 4 | import java.net.DatagramSocket 5 | import java.net.InetAddress 6 | import play.Logger 7 | import scala.util.Random 8 | import play.api.Play 9 | 10 | /** 11 | * Configuration trait for the [[play.modules.statsd.api.StatsdClient]]. 12 | * 13 | * Provides to the client the prefix for all stats sent to statsd and mechanism 14 | * for sending stats over the network. 15 | */ 16 | trait StatsdClientCake { 17 | // Used as the prefix for all stats. 18 | protected val statPrefix: String 19 | 20 | // Used to actually send a stat to statsd. 21 | protected val send: Function1[String, Unit] 22 | 23 | // Used to time an operation. 24 | protected def now(): Long 25 | 26 | // Used to determine whether or not a sampled stat should be sent. 27 | protected def nextFloat(): Float 28 | } 29 | 30 | /** 31 | * Real implementation of [[play.modules.statsd.api.StatsdClientCake]]. 32 | * 33 | * This implementation: 34 | * - Reads in values from `conf/application.conf` 35 | * - Sends stats using a `DatagramSocket` to statsd server. 36 | */ 37 | private[api] trait RealStatsdClientCake extends StatsdClientCake { 38 | 39 | // The property name for whether or not the statsd sending should be enabled. 40 | private val StatsdEnabledProperty = "statsd.enabled" 41 | 42 | // The property name for the statsd port. 43 | private val PortProperty = "statsd.port" 44 | 45 | // The property name for the statsd host. 46 | private val HostnameProperty = "statsd.host" 47 | 48 | // The property name for the application stat prefix. 49 | private val StatPrefixProperty = "statsd.stat.prefix" 50 | 51 | // Use scala's Random util for nextFloat. 52 | private lazy val random = new Random 53 | 54 | // The stat prefix used by the client. 55 | override val statPrefix = { 56 | Play.maybeApplication flatMap { _.configuration.getString(StatPrefixProperty) } getOrElse { 57 | Logger.warn("No stat prefix configured, using default of statsd") 58 | "statsd" 59 | } 60 | } 61 | 62 | /** 63 | * Use `System.currentTimeMillis()` to get the current time. 64 | */ 65 | override def now(): Long = System.currentTimeMillis() 66 | 67 | /** 68 | * Use scala's [[scala.util.Random]] util for `nextFloat`. 69 | */ 70 | override def nextFloat(): Float = random.nextFloat() 71 | 72 | /** 73 | * Expose a `send` function to the client. It is configured with the hostname and port. 74 | * 75 | * If statsd isn't enabled, it will be a noop function. 76 | */ 77 | override lazy val send: Function1[String, Unit] = { 78 | try { 79 | // Check if Statsd sending is enabled. 80 | val enabled = please.booleanConfig(StatsdEnabledProperty) 81 | if (enabled) { 82 | // Initialize the socket, host, and port to be used to send the data. 83 | val socket = new DatagramSocket 84 | val hostname = please.config(HostnameProperty) 85 | val host = InetAddress.getByName(hostname) 86 | val port = please.intConfig(PortProperty) 87 | 88 | // Return the real send function, partially applied with the 89 | // socket, host, and port so the client only has to call "send(stat)". 90 | socketSend(socket, host, port) _ 91 | } else { 92 | Logger.warn("Send will be NOOP because %s is not enabled".format( 93 | StatsdEnabledProperty)) 94 | noopSend _ 95 | } 96 | 97 | } catch { 98 | // If there is any error configuring the send function, log a warning 99 | // but don't throw an error. Use a noop function for all sends. 100 | case error: Throwable => 101 | Logger.warn("Send will NOOP because of configuration problem.", error) 102 | noopSend _ 103 | } 104 | } 105 | 106 | /** 107 | * Send the stat in a [[java.net.DatagramPacket]] to statsd. 108 | */ 109 | private def socketSend( 110 | socket: DatagramSocket, host: InetAddress, port: Int)(stat: String) { 111 | try { 112 | val data = stat.getBytes 113 | socket.send(new DatagramPacket(data, data.length, host, port)) 114 | } catch { 115 | case error: Throwable => Logger.error("", error) 116 | } 117 | } 118 | 119 | /** 120 | * Don't do anything. Used if statsd isn't enabled or on config errors. 121 | */ 122 | private def noopSend(stat: String) = Unit 123 | } 124 | -------------------------------------------------------------------------------- /statsd/src/test/scala/play/modules/statsd/api/StatsdSpec.scala: -------------------------------------------------------------------------------- 1 | package play.modules.statsd.api 2 | 3 | import play.api.test.FakeApplication 4 | import java.net.{SocketTimeoutException, DatagramPacket, DatagramSocket} 5 | import play.api.test.Helpers.running 6 | import org.specs2.mutable.{Specification, BeforeAfter} 7 | 8 | class StatsdSpec extends Specification { 9 | sequential 10 | "Statsd" should { 11 | "send gauge value" in new Setup { 12 | running(fakeApp) { 13 | Statsd.gauge("test", 42) 14 | receive() mustEqual "statsd.test:42|g" 15 | } 16 | } 17 | "send delta gauge value" in new Setup { 18 | running(fakeApp) { 19 | Statsd.gauge("test", 10, true) 20 | receive() mustEqual "statsd.test:+10|g" 21 | Statsd.gauge("test", -10, true) 22 | receive() mustEqual "statsd.test:-10|g" 23 | } 24 | } 25 | "send increment by one message" in new Setup { 26 | running(fakeApp) { 27 | Statsd.increment("test") 28 | receive() mustEqual "statsd.test:1|c" 29 | } 30 | } 31 | "send increment by more message" in new Setup { 32 | running(fakeApp) { 33 | Statsd.increment("test", 10) 34 | receive() mustEqual "statsd.test:10|c" 35 | } 36 | } 37 | // There is a 0.0001% chance that the following two tests might fail 38 | "hopefully send a message when sampling rate is only just below 1" in new Setup { 39 | running(fakeApp) { 40 | Statsd.increment("test", 10, 0.999999) 41 | receive() mustEqual "statsd.test:10|c|@0.999999" 42 | } 43 | } 44 | "hopefully not send a message when sampling rate is only just above 0" in new Setup { 45 | running(fakeApp) { 46 | Statsd.increment("test", 10, 0.000001) 47 | verifyNothingReceived() 48 | } 49 | } 50 | "send timing message" in new Setup { 51 | running(fakeApp) { 52 | Statsd.timing("test", 1234) 53 | receive() mustEqual "statsd.test:1234|ms" 54 | } 55 | } 56 | "execute timed function and report" in new Setup { 57 | running(fakeApp) { 58 | Statsd.time("test", 1.0) { Thread.sleep(10) } 59 | val msg = receive() 60 | msg must ((_:String).startsWith("statsd.test:"), "incorrect prefix") 61 | msg must ((_:String).endsWith("|ms"), "incorrect postfix") 62 | val time = msg.stripPrefix("statsd.test:").stripSuffix("|ms").toInt 63 | time must be_>=(10) 64 | } 65 | } 66 | "return return value of timed function" in new Setup { 67 | running(fakeApp) { 68 | Statsd.time("test", 1.0) { "blah" } mustEqual "blah" 69 | } 70 | } 71 | "do nothing if there's no running application" in new Setup { 72 | // A separate singleton, that ensures it's not configured with the configuration that was available during the 73 | // other tests 74 | object TestStatsd extends StatsdClient with RealStatsdClientCake 75 | TestStatsd.increment("blah") 76 | verifyNothingReceived() 77 | } 78 | } 79 | 80 | trait Setup extends BeforeAfter { 81 | val PORT = 57475 82 | val fakeApp = FakeApplication(additionalConfiguration = Map( 83 | "ehcacheplugin" -> "disabled", 84 | "statsd.enabled" -> "true", 85 | "statsd.host" -> "localhost", 86 | "statsd.port" -> PORT.toString)) 87 | lazy val mockStatsd = { 88 | val socket = new DatagramSocket(PORT) 89 | socket.setSoTimeout(200) 90 | socket 91 | } 92 | 93 | def receive() = { 94 | val buf: Array[Byte] = new Array[Byte](1024) 95 | val packet = new DatagramPacket(buf, buf.length) 96 | try { 97 | mockStatsd.receive(packet) 98 | } 99 | catch { 100 | case s: SocketTimeoutException => failure("Didn't receive message within 200ms") 101 | } 102 | new String(packet.getData, 0, packet.getLength) 103 | } 104 | 105 | def verifyNothingReceived() { 106 | val buf: Array[Byte] = new Array[Byte](1024) 107 | val packet = new DatagramPacket(buf, buf.length) 108 | try { 109 | mockStatsd.receive(packet) 110 | failure("Unexpected packet received: " + new String(packet.getData, 0, packet.getLength)) 111 | } 112 | catch { 113 | case s: SocketTimeoutException => Unit 114 | } 115 | } 116 | 117 | def before { 118 | mockStatsd 119 | } 120 | 121 | def after { 122 | mockStatsd.close() 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /inject/README.md: -------------------------------------------------------------------------------- 1 | # Play 2.1 2 | this plugin now is obsolate. Use ```play.GlobalSetting#getControllerInstance``` instead. 3 | 4 | 5 | # For older verions of Play 6 | 7 | # Manual Injection Plugin 8 | 9 | This plugin provides support for manual dependency injection. Injection points are defined using ```@Provides``` (and ```@Singleton```) annotations in a class called ```module.Dependencies```. 10 | These would be injected into a preconfigured package (```controllers``` by default) 11 | 12 | # Features 13 | 14 | * Allows static field injection to a preconfigured package (```controllers``` by default) ie 15 | 16 | (see ```sample``` for a full example) 17 | 18 | ```java 19 | // define your dependencies in module/Dependencies.java 20 | public class Dependencies { 21 | 22 | @Provides 23 | @Singleton 24 | public Something makeSomething() { 25 | return new SpecialSomething(); 26 | } 27 | 28 | @Provides 29 | @Singleton 30 | public MyService makeService(Something s) { 31 | return new SpecialService(s); 32 | } 33 | 34 | 35 | } 36 | ``` 37 | 38 | ```java 39 | //any controller 40 | public class Application extends Controller { 41 | 42 | @Inject static MyService s; 43 | 44 | public static Result index() { 45 | return ok(index.render(s.demonstrate())); 46 | } 47 | 48 | } 49 | 50 | ``` 51 | 52 | * or you can use constructor injection with a delege 53 | (see ```sample_without_static_field``` for a full example) 54 | 55 | in app/controllers/Application.java: 56 | 57 | ```java 58 | public class Application extends Controller { 59 | 60 | private MyService s; 61 | 62 | @Inject public Application( MyService s) { 63 | this.s=s; 64 | } 65 | 66 | public Result index() { 67 | return ok(index.render(s.demonstrate())); 68 | } 69 | 70 | } 71 | ``` 72 | 73 | in app/module/Dependencies.java: 74 | 75 | ```java 76 | public class Dependencies { 77 | 78 | public static InjectPlugin inject() { 79 | return Play.application().plugin(InjectPlugin.class); 80 | } 81 | 82 | //this is needed for each controller 83 | public static controllers.Application application() { 84 | return inject().getInstance(controllers.Application.class); 85 | } 86 | 87 | @Provides 88 | @Singleton 89 | public Something makeSomething() { 90 | return new Something() { 91 | public String noWhat() { 92 | return "yay"; 93 | } 94 | }; 95 | } 96 | 97 | @Provides 98 | @Singleton 99 | public MyService makeService(Something s) { 100 | return new MyService(s) { 101 | public String demonstrate() { return s.noWhat();} 102 | }; 103 | } 104 | 105 | } 106 | ``` 107 | 108 | 109 | in ```conf/routes```: 110 | 111 | ``` 112 | GET / module.Dependencies.application.index 113 | ``` 114 | 115 | * Dependency modules are configurable (by default it's ```module.Dependencies```) 116 | 117 | * Allows direct access to the factory method ie 118 | 119 | ```java 120 | //in Global.java 121 | play.Play.application().plugin(InjectPlugin.class).getInstance(MyServiceInterface.class) 122 | ``` 123 | 124 | # How to install 125 | 126 | * add 127 | 128 | play 2.0.2: 129 | 130 | ```"com.typesafe" % "play-plugins-inject" % "2.0.3"``` to your dependencies 131 | 132 | play 2.0.1: 133 | 134 | ```"com.typesafe" % "play-plugins-inject" % "2.0.2"``` to your dependencies 135 | 136 | 137 | * create a file called ```play.plugins``` in your ```app/conf``` directory 138 | 139 | * add ```1500:com.typesafe.plugin.inject.ManualInjectionPlugin``` 140 | 141 | * that's it 142 | 143 | # Testing 144 | 145 | * testing can be achieved two ways while using the static field injection: 146 | 147 | * making the injected dependencies anything but private 148 | 149 | * injected dependencies can be private in which case a new constructor could be added to controllers which could be used to inject the mocked dependencies 150 | (I would recommend the former solution) 151 | * if the static delegate approach was used, one can directly test the controller 152 | 153 | 154 | 155 | # Sample 156 | 157 | for an example, see the bundled sample app 158 | 159 | 160 | ## Licence 161 | 162 | This software is licensed under the Apache 2 license, quoted below. 163 | 164 | Copyright 2012 Typesafe (http://www.typesafe.com). 165 | 166 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 167 | 168 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 169 | -------------------------------------------------------------------------------- /redis/src/main/scala/com/typesafe/play/redis/RedisCacheApi.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.play.redis 2 | 3 | import java.io._ 4 | import javax.inject.{Inject, Singleton} 5 | 6 | import biz.source_code.base64Coder.Base64Coder 7 | import org.sedis.Pool 8 | import play.api.Logger 9 | import play.api.cache.CacheApi 10 | 11 | import scala.concurrent.duration.Duration 12 | import scala.reflect.ClassTag 13 | 14 | 15 | @Singleton 16 | class RedisCacheApi @Inject()(val namespace: String, sedisPool: Pool, classLoader: ClassLoader) extends CacheApi { 17 | 18 | private val namespacedKey: (String => String) = { x => s"$namespace::$x" } 19 | 20 | def get[T](userKey: String)(implicit ct: ClassTag[T]): Option[T] = { 21 | Logger.trace(s"Reading key ${namespacedKey(userKey)}") 22 | 23 | try { 24 | val rawData = sedisPool.withJedisClient { client => client.get(namespacedKey(userKey)) } 25 | rawData match { 26 | case null => 27 | None 28 | case _ => 29 | val data: Seq[String] = rawData.split("-") 30 | val bytes = Base64Coder.decode(data.last) 31 | data.head match { 32 | case "oos" => Some(withObjectInputStream(bytes)(_.readObject().asInstanceOf[T])) 33 | case "string" => Some(withDataInputStream(bytes)(_.readUTF().asInstanceOf[T])) 34 | case "int" => Some(withDataInputStream(bytes)(_.readInt().asInstanceOf[T])) 35 | case "long" => Some(withDataInputStream(bytes)(_.readLong().asInstanceOf[T])) 36 | case "boolean" => Some(withDataInputStream(bytes)(_.readBoolean().asInstanceOf[T])) 37 | case _ => throw new IOException(s"was not able to recognize the type of serialized value. The type was ${data.head} ") 38 | } 39 | } 40 | } catch { 41 | case ex: Exception => 42 | Logger.warn("could not deserialize key:" + namespacedKey(userKey), ex) 43 | None 44 | } 45 | } 46 | 47 | def getOrElse[A: ClassTag](userKey: String, expiration: Duration)(orElse: => A) = { 48 | get[A](userKey).getOrElse { 49 | val value = orElse 50 | set(userKey, value, expiration) 51 | value 52 | } 53 | } 54 | 55 | def remove(userKey: String): Unit = sedisPool.withJedisClient(_.del(namespacedKey(userKey))) 56 | 57 | def set(userKey: String, value: Any, expiration: Duration) { 58 | val expirationInSec = if (expiration == Duration.Inf) 0 else expiration.toSeconds.toInt 59 | val key = namespacedKey(userKey) 60 | 61 | var oos: ObjectOutputStream = null 62 | var dos: DataOutputStream = null 63 | try { 64 | val baos = new ByteArrayOutputStream() 65 | val prefix = value match { 66 | case _: String => 67 | dos = new DataOutputStream(baos) 68 | dos.writeUTF(value.asInstanceOf[String]) 69 | "string" 70 | case _: Int => 71 | dos = new DataOutputStream(baos) 72 | dos.writeInt(value.asInstanceOf[Int]) 73 | "int" 74 | case _: Long => 75 | dos = new DataOutputStream(baos) 76 | dos.writeLong(value.asInstanceOf[Long]) 77 | "long" 78 | case _: Boolean => 79 | dos = new DataOutputStream(baos) 80 | dos.writeBoolean(value.asInstanceOf[Boolean]) 81 | "boolean" 82 | case _: Serializable => 83 | oos = new ObjectOutputStream(baos) 84 | oos.writeObject(value) 85 | oos.flush() 86 | "oos" 87 | case _ => 88 | throw new IOException("could not serialize: " + value.toString) 89 | } 90 | 91 | val redisV = prefix + "-" + new String(Base64Coder.encode(baos.toByteArray)) 92 | Logger.trace(s"Setting key $key to $redisV") 93 | 94 | sedisPool.withJedisClient { client => 95 | client.set(key, redisV) 96 | if (expirationInSec != 0) client.expire(key, expirationInSec) 97 | } 98 | } catch { 99 | case ex: IOException => 100 | Logger.warn("could not serialize key:" + key + " and value:" + value.toString + " ex:" + ex.toString) 101 | } finally { 102 | if (oos != null) oos.close() 103 | if (dos != null) dos.close() 104 | } 105 | } 106 | 107 | private class ClassLoaderObjectInputStream(stream: InputStream) extends ObjectInputStream(stream) { 108 | override protected def resolveClass(desc: ObjectStreamClass) = { 109 | Class.forName(desc.getName, false, classLoader) 110 | } 111 | } 112 | 113 | private def withDataInputStream[T](bytes: Array[Byte])(f: DataInputStream => T): T = { 114 | val dis = new DataInputStream(new ByteArrayInputStream(bytes)) 115 | try f(dis) finally dis.close() 116 | } 117 | 118 | private def withObjectInputStream[T](bytes: Array[Byte])(f: ObjectInputStream => T): T = { 119 | val ois = new ClassLoaderObjectInputStream(new ByteArrayInputStream(bytes)) 120 | try f(ois) finally ois.close() 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /dust/sample/public/javascripts/dust-core-0.3.0.min.js: -------------------------------------------------------------------------------- 1 | // 2 | // Dust - Asynchronous Templating v0.3.0 3 | // http://akdubya.github.com/dustjs 4 | // 5 | // Copyright (c) 2010, Aleksander Williams 6 | // Released under the MIT License. 7 | // 8 | 9 | var dust={}; 10 | (function(d){function h(a,b,c){this.stack=a;this.global=b;this.blocks=c}function k(a,b,c,e){this.tail=b;this.isObject=!d.isArray(a)&&a&&typeof a==="object";this.head=a;this.index=c;this.of=e}function l(a){this.head=new f(this);this.callback=a;this.out=""}function j(){this.head=new f(this)}function f(a,b,c){this.root=a;this.next=b;this.data="";this.flushable=false;this.taps=c}function m(a,b){this.head=a;this.tail=b}d.cache={};d.register=function(a,b){if(a)d.cache[a]=b};d.render=function(a,b,c){c=(new l(c)).head; 11 | d.load(a,c,h.wrap(b)).end()};d.stream=function(a,b){var c=new j;d.nextTick(function(){d.load(a,c.head,h.wrap(b)).end()});return c};d.renderSource=function(a,b,c){return d.compileFn(a)(b,c)};d.compileFn=function(a,b){var c=d.loadSource(d.compile(a,b));return function(e,g){var i=g?new l(g):new j;d.nextTick(function(){c(i.head,h.wrap(e)).end()});return i}};d.load=function(a,b,c){var e=d.cache[a];if(e)return e(b,c);else{if(d.onLoad)return b.map(function(g){d.onLoad(a,function(i,n){if(i)return g.setError(i); 12 | d.cache[a]||d.loadSource(d.compile(n,a));d.cache[a](g,c).end()})});return b.setError(Error("Template Not Found: "+a))}};d.loadSource=function(a){return eval(a)};d.isArray=Array.isArray?Array.isArray:function(a){return Object.prototype.toString.call(a)=="[object Array]"};d.nextTick=function(a){setTimeout(a,0)};d.isEmpty=function(a){if(d.isArray(a)&&!a.length)return true;if(a===0)return false;return!a};d.filter=function(a,b,c){if(c)for(var e=0,g=c.length;e\"]/),p=/&/g,q=//g,s=/\"/g;d.escapeHtml=function(a){if(typeof a==="string"){if(!o.test(a))return a;return a.replace(p,"&").replace(q,"<").replace(r,">").replace(s,""")}return a}; 21 | var t=/\\/g,u=/\r/g,v=/\u2028/g,w=/\u2029/g,x=/\n/g,y=/\f/g,z=/'/g,A=/"/g,B=/\t/g;d.escapeJs=function(a){if(typeof a==="string")return a.replace(t,"\\\\").replace(A,'\\"').replace(z,"\\'").replace(u,"\\r").replace(v,"\\u2028").replace(w,"\\u2029").replace(x,"\\n").replace(y,"\\f").replace(B,"\\t");return a}})(dust);if(typeof exports!=="undefined"){typeof process!=="undefined"&&require("./server")(dust);module.exports=dust}; 22 | -------------------------------------------------------------------------------- /statsd/documentation/manual/home.textile: -------------------------------------------------------------------------------- 1 | h1. statsd 2 | 3 | This is a simple **statsd** module for **Play Framework 2**. It pulls in configuration from @conf/application.conf@ and provides a singleton object @Statsd@ with methods for **counter** and **timing** calls to **statsd**. It provides both a Scala and Java interface. Similar to the Play 2 convention, the Scala interface is called @play.modules.statsd.api.Statsd@, and the Java interface is @play.modules.statsd.Statsd@. 4 | 5 | h2. Getting started 6 | 7 | To install, add @"com.typesafe.play.plugins" %% "play-statsd" % "2.3.0"@ to your dependencies, for example: 8 | 9 | bc.. val appDendencies = Seq(@"com.typesafe.play.plugins" %% "play-statsd" % "2.3.0") 10 | 11 | h2. Configuration 12 | 13 | The following are configuration flags that belong in @conf/application.conf@: 14 | 15 | * @statsd.enabled@: Should be @true@ to use this module. Can be @false@ for testing. 16 | * @statsd.stat.prefix@: The prefix for all stats sent by this app. They will appear in a folder of the same name on graphite. 17 | * @statsd.routes.prefix@: The prefix to be attached to all routes logged by the StatsdFilter. By default this is "routes". This prefix is in addition to the main prefix (so if that's foo, then the default will be foo.routes). 18 | * @statsd.routes.pathSeparator@: The separator character that's used to replace "/" in routes logged by the StatsdFilter. By default this is ".". 19 | * @statsd.routes.combined.prefix@: The prefix to be attached to combined http request logging. By default this is "routes.combined". 20 | * @statsd.host@: The hostname of the statsd server. 21 | * @statsd.port@: The port for the statsd server. 22 | 23 | p(note). If there are any configuration problems (missing or unparseable settings), there will be a warning the first time the module is used but will not cause an error in your app. 24 | 25 | h2. Scala Usage 26 | 27 | To use this module, first add this import: 28 | 29 | @import play.modules.statsd.api.Statsd@ 30 | 31 | Now you can call it like this: 32 | 33 | bc.. Statsd.increment("my.stat") // Increment my.stat by 1 34 | Statsd.increment("my.bigger.stat", value = 100) // Increment my.bigger.stat by 100 35 | Statsd.increment("my.frequent.stat", samplingRate = 0.1) // Increment my.frequent.stat 10% of the time 36 | Statsd.timing("my.operation", 100) // my.operation took 100 ms 37 | Statsd.timing("my.frequent.operation", 10, 0.5) // my operation took 50 ms. Send this stat 50% of the time 38 | Statsd.time("my.operation.i.dont.want.to.time.myself") { 39 | // do some stuff... 40 | } // This will get timed automatically. 41 | Statsd.gauge("my.value", 42) // Record 42 for my.value 42 | Statsd.gauge("my.value", 10, true) // Increment my.value by 10 43 | Statsd.gauge("my.value", -10, true) // Decrement my.value by 10 44 | 45 | p(note). Any errors will be logged, but will not cause the app to fail. 46 | 47 | h2. Java Usage 48 | 49 | To use this module, first add this import: 50 | 51 | @import play.modules.statsd.Statsd;@ 52 | 53 | Now you can call it like this: 54 | 55 | bc.. Statsd.increment("my.stat"); // Increment my.stat by 1 56 | Statsd.increment("my.bigger.stat", 100); // Increment my.bigger.stat by 100 57 | Statsd.increment("my.frequent.stat", 0.1); // Increment my.frequent.stat 10% of the time 58 | Statsd.timing("my.operation", 100); // my.operation took 100 ms 59 | Statsd.timing("my.frequent.operation", 10, 0.5); // my operation took 50 ms. Send this stat 50% of the time 60 | String result = Statsd.time("my.operation.i.dont.want.to.time.myself", new F.Function0() { 61 | public String apply() { 62 | return "some result"; 63 | }}); // This will get timed automatically. 64 | Statsd.gauge("my.value", 42L) // Record 42 for my.value 65 | Statsd.gauge("my.value", 10L, true) // Increment my.value by 10 66 | Statsd.gauge("my.value", -10L, true) // Decrement my.value by 10 67 | 68 | p(note). Any errors will be logged, but will not cause the app to fail. 69 | 70 | h2. StatsdFilter 71 | 72 | This module comes with an optional filter that can be used to log request counts and time requests. It maps requests according to their configured path and method to a statsd key, using the following rules: 73 | 74 | bc.. GET /foo/bar -> routes.foo.bar.get 75 | GET /foo/:param -> routes.foo.param.get 76 | GET /foo/*path -> routes.foo.path.get 77 | POST /foo/$id<[0-9]+> -> routes.foo.id.post 78 | GET / -> routes.get 79 | # @statsd.key some.custom.key 80 | GET /foo/bar -> routes.some.custom.key 81 | 82 | In addition, the following stats overall stats are recorded: 83 | 84 | @routes.combined.200@ - The count of each response by status code. 85 | @routes.combined.success@ - The count of each successful response. This includes 4xx status codes, since the server still handled them successfully. 86 | @routes.combined.error@ - The count of error responses. This is any response with a 5xx status code. 87 | @routes.combined.time@ - The time for all requests. 88 | @routes.combined.handlerNotFound@ - The count and time of all requests for which no handler was found to handle them. 89 | 90 | Note that the time a request takes is defined as the amount of time from when the request headers are first received, through to when the response header is generated. It includes the time it takes to receive the body of the request, but does not include the time it takes to send the body of the response. 91 | 92 | Websocket requests are not reported. 93 | 94 | h3. Usage in Scala 95 | 96 | The easiest way to use the filter in Scala applications is to use the @WithFilters@ helper in your @Global@ object, like this: 97 | 98 | bq.. object Global extends WithFilters(new play.modules.statsd.api.StatsdFilter()) { 99 | ... 100 | } 101 | 102 | h3. Usage in Java 103 | 104 | Just return the @StatsdFilter@ class from the @filters@ method of your @Global@ object: 105 | 106 | bq.. public class Global extends GlobalSettings { 107 | public Class[] filters() { 108 | return new Class[] {play.modules.statsd.StatsdFilter.class}; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /statsd/src/main/scala/play/modules/statsd/api/StatsdClient.scala: -------------------------------------------------------------------------------- 1 | package play.modules.statsd.api 2 | 3 | import play.Logger 4 | import scala.util.control.NonFatal 5 | 6 | /** 7 | * Trait defining the statsd interface. It defines the two stats calls in 8 | * statsd: `increment` and `timing`. It must be instantiated with 9 | * [[play.modules.statsd.api.StatsdClientCake]] which handles the sending of stats over the network. 10 | * 11 | * Two stats-related function are supported: 12 | * - `increment`: Increment a given stat key. 13 | * - `timing`: Sending timing info for a given operation key. 14 | * 15 | * For both, an optional `samplingRate` parameter can be provided. 16 | * For parameters between 0 and 1.0, the client will send the stat 17 | * `samplingRate * 100` of the time. This is useful for some stats that 18 | * occur extremely frequently and therefore put too much load on the statsd 19 | * server. 20 | * 21 | * The functionality is exposed to Play Apps using the [[play.modules.statsd.Statsd]] object. 22 | */ 23 | trait StatsdClient { 24 | self: StatsdClientCake => 25 | 26 | // Suffix for increment stats. 27 | private val IncrementSuffix = "c" 28 | 29 | // Suffix for timing stats. 30 | private val TimingSuffix = "ms" 31 | 32 | // Suffix for gauge stats. 33 | private val GaugeSuffix = "g" 34 | 35 | /** 36 | * Increment a given stat key. Optionally give it a value and sampling rate. 37 | * 38 | * @param key The stat key to be incremented. 39 | * @param value The amount by which to increment the stat. Defaults to 1. 40 | * @param samplingRate The probability for which to increment. Defaults to 1. 41 | */ 42 | def increment(key: String, value: Long = 1, samplingRate: Double = 1.0) { 43 | safely { maybeSend(statFor(key, value, IncrementSuffix, samplingRate), samplingRate) } 44 | } 45 | 46 | /** 47 | * Timing data for given stat key. Optionally give it a sampling rate. 48 | * 49 | * @param key The stat key to be timed. 50 | * @param millis The number of milliseconds the operation took. 51 | * @param samplingRate The probability for which to increment. Defaults to 1. 52 | */ 53 | def timing(key: String, millis: Long, samplingRate: Double = 1.0) { 54 | safely { maybeSend(statFor(key, millis, TimingSuffix, samplingRate), samplingRate) } 55 | } 56 | 57 | /** 58 | * Time a given operation and send the resulting stat. 59 | * 60 | * @param key The stat key to be timed. 61 | * @param samplingRate The probability for which to increment. Defaults to 1. 62 | * @param timed An arbitrary block of code to be timed. 63 | * @return The result of the timed operation. 64 | */ 65 | def time[T](key: String, samplingRate: Double = 1.0)(timed: => T): T = { 66 | val start = now() 67 | val result = timed 68 | val finish = now() 69 | timing(key, finish - start, samplingRate) 70 | result 71 | } 72 | 73 | /** 74 | * Record the given value. 75 | * 76 | * @param key The stat key to update. 77 | * @param value The value to record for the stat. 78 | */ 79 | def gauge(key: String, value: Long) { 80 | safely { maybeSend(statFor(key, value, GaugeSuffix, 1.0), 1.0) } 81 | } 82 | 83 | /** 84 | * Record the given value. 85 | * 86 | * @param key The stat key to update. 87 | * @param value The value to record for the stat. 88 | */ 89 | def gauge(key: String, value: Long, delta: Boolean) { 90 | if (!delta) { 91 | safely { maybeSend(statFor(key, value, GaugeSuffix, 1.0), 1.0) } 92 | } else { 93 | if (value >= 0) { 94 | safely { maybeSend(statFor(key, "+".concat(value.toString), GaugeSuffix, 1.0), 1.0) } 95 | } else { 96 | safely { maybeSend(statFor(key, value.toString, GaugeSuffix, 1.0), 1.0) } 97 | } 98 | } 99 | } 100 | 101 | /* 102 | * **************************************************************** 103 | * PRIVATE IMPLEMENTATION DETAILS 104 | * **************************************************************** 105 | */ 106 | 107 | /* 108 | * Creates the stat string to send to statsd. 109 | * For counters, it provides something like {@code key:value|c}. 110 | * For timing, it provides something like {@code key:millis|ms}. 111 | * If sampling rate is less than 1, it provides something like {@code key:value|type|@rate} 112 | */ 113 | private def statFor(key: String, value: Long, suffix: String, samplingRate: Double): String = { 114 | statFor(key, value.toString, suffix, samplingRate) 115 | } 116 | 117 | /* 118 | * Creates the stat string to send to statsd. 119 | * For counters, it provides something like {@code key:value|c}. 120 | * For timing, it provides something like {@code key:millis|ms}. 121 | * If sampling rate is less than 1, it provides something like {@code key:value|type|@rate} 122 | */ 123 | private def statFor(key: String, value: String, suffix: String, samplingRate: Double): String = { 124 | samplingRate match { 125 | case x if x >= 1.0 => "%s.%s:%s|%s".format(statPrefix, key, value, suffix) 126 | case _ => "%s.%s:%s|%s|@%f".format(statPrefix, key, value, suffix, samplingRate) 127 | } 128 | } 129 | 130 | /* 131 | * Probabilistically calls the {@code send} function. If the sampling rate 132 | * is 1.0 or greater, we always call send. Use a random number call send 133 | * function {@code (samplingRate * 100)%} of the time. 134 | */ 135 | private def maybeSend(stat: String, samplingRate: Double) { 136 | if (samplingRate >= 1.0 || nextFloat() < samplingRate) { 137 | send(stat) 138 | } 139 | } 140 | 141 | /* 142 | * Safety net for operations that shouldn't throw exceptions. 143 | */ 144 | private def safely(operation: => Unit) { 145 | try { 146 | operation 147 | } catch { 148 | case NonFatal(error) => Logger.warn("Unhandled throwable sending stat.", error) 149 | } 150 | } 151 | } 152 | 153 | /** 154 | * Wrap the [[play.modules.statsd.api.StatsdClient]] trait configured with 155 | * [[play.modules.statsd.api.RealStatsdClientCake]] in an object to make it available to the app. 156 | */ 157 | object Statsd extends StatsdClient with RealStatsdClientCake 158 | -------------------------------------------------------------------------------- /statsd/sample/sample-statsd/test/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import java.net.{SocketTimeoutException, DatagramPacket, DatagramSocket} 4 | import org.specs2.mutable._ 5 | import play.api.test.Helpers._ 6 | import play.api.test._ 7 | import org.specs2.execute.{AsResult, Result} 8 | import collection.mutable.ListBuffer 9 | import play.api.libs.ws.WS 10 | import concurrent.Await 11 | import concurrent.duration.Duration 12 | 13 | object IntegrationTestSpec extends Specification { 14 | "statsd filters" should { 15 | 16 | "report stats on /" in new Setup { 17 | makeRequest("/") 18 | receive(count("sample.routes.get"), timing("sample.routes.get"), combinedTime, combinedSuccess, combined200) 19 | } 20 | 21 | "report stats on simple path" in new Setup { 22 | makeRequest("/foo/bar") 23 | receive(count("sample.routes.foo.bar.get"), timing("sample.routes.foo.bar.get"), combinedTime, combinedSuccess, combined200) 24 | } 25 | 26 | "report stats on path with dynamic end" in new Setup { 27 | makeRequest("/single/end/blah") 28 | receive(count("sample.routes.single.end.param.get"), timing("sample.routes.single.end.param.get"), combinedTime, combinedSuccess, combined200) 29 | } 30 | 31 | "report stats on path with dynamic middle" in new Setup { 32 | makeRequest("/single/middle/blah/f") 33 | receive(count("sample.routes.single.middle.param.f.get"), timing("sample.routes.single.middle.param.f.get"), combinedTime, combinedSuccess, combined200) 34 | } 35 | 36 | "report stats on path with regex" in new Setup { 37 | makeRequest("/regex/21") 38 | receive(count("sample.routes.regex.param.get"), timing("sample.routes.regex.param.get"), combinedTime, combinedSuccess, combined200) 39 | } 40 | 41 | "report stats on path with wildcard" in new Setup { 42 | makeRequest("/rest/blah/blah") 43 | receive(count("sample.routes.rest.param.get"), timing("sample.routes.rest.param.get"), combinedTime, combinedSuccess, combined200) 44 | } 45 | 46 | "report stats on path with multiple params" in new Setup { 47 | makeRequest("/multiple/foo/bar") 48 | receive(count("sample.routes.multiple.param1.param2.get"), timing("sample.routes.multiple.param1.param2.get"), combinedTime, combinedSuccess, combined200) 49 | } 50 | 51 | "report stats on async action" in new Setup { 52 | makeRequest("/async") 53 | receive(count("sample.routes.async.get"), timing("sample.routes.async.get"), combinedTime, combinedSuccess, combined200) 54 | } 55 | 56 | "report stats on failure" in new Setup { 57 | makeWsRequest("/sync/failure") 58 | receive(count("sample.routes.sync.failure.get"), timing("sample.routes.sync.failure.get"), combinedTime, combinedError, combined500) 59 | } 60 | 61 | "report stats on failure thrown in async" in new Setup { 62 | makeWsRequest("/async/failure") 63 | receive(count("sample.routes.async.failure.get"), timing("sample.routes.async.failure.get"), combinedTime, combinedError, combined500) 64 | } 65 | 66 | "report stats on action returning 503" in new Setup { 67 | makeRequest("/error", 503) 68 | receive(count("sample.routes.error.get"), timing("sample.routes.error.get"), combinedTime, combinedError, combined503) 69 | } 70 | 71 | "report stats on handlerNotFound" in new Setup { 72 | makeWsRequest("/does/not/exist", 404) 73 | receive(count("sample.routes.combined.handlerNotFound"), timing("sample.routes.combined.handlerNotFound", 0), timing("sample.routes.combined.time", 0), combinedSuccess, combined404) 74 | } 75 | 76 | } 77 | 78 | def makeRequest(path: String, expectedStatus: Int = 200) { 79 | status(route(FakeRequest("GET", path)).get) must_== expectedStatus 80 | } 81 | 82 | def makeWsRequest(path: String, expectedStatus: Int = 500) { 83 | Await.result(WS.url("http://localhost:9001" + path).get(), Duration.apply("2s")).status must_== expectedStatus 84 | } 85 | 86 | trait Setup extends Around { 87 | lazy val PORT = 57476 88 | implicit lazy val fakeApp = FakeApplication(additionalConfiguration = Map( 89 | "statsd.enabled" -> "true", 90 | "statsd.host" -> "localhost", 91 | "statsd.port" -> PORT.toString, 92 | "statsd.stat.prefix" -> "sample")) 93 | lazy val mockStatsd = { 94 | val socket = new DatagramSocket(PORT) 95 | socket.setSoTimeout(2000) 96 | socket 97 | } 98 | 99 | def receive(ps: PartialFunction[String, Unit]*) = { 100 | val expects = ListBuffer(ps :_*) 101 | for (i <- 1 until ps.size + 1) { 102 | val buf: Array[Byte] = new Array[Byte](1024) 103 | val packet = new DatagramPacket(buf, buf.length) 104 | try { 105 | mockStatsd.receive(packet) 106 | } 107 | catch { 108 | case s: SocketTimeoutException => failure("Didn't receive message no " + i + " within 2s") 109 | } 110 | val data = new String(packet.getData, 0, packet.getLength) 111 | val matched = expects.collectFirst { 112 | case expect if expect.isDefinedAt(data) => { 113 | expects -= expect 114 | expect(data) 115 | } 116 | } 117 | matched aka("No matching assertion for data: '%s' ".format(data)) must beSome[Unit] 118 | } 119 | 120 | expects must beEmpty 121 | } 122 | 123 | 124 | def around[T](t: => T)(implicit evidence$1: AsResult[T]) = running(TestServer(9001, fakeApp)) { 125 | mockStatsd 126 | try { 127 | AsResult(t) 128 | } finally { 129 | mockStatsd.close() 130 | } 131 | } 132 | } 133 | 134 | def combinedSuccess = count("sample.routes.combined.success") 135 | def combinedError = count("sample.routes.combined.error") 136 | def combined200 = count("sample.routes.combined.200") 137 | def combined404 = count("sample.routes.combined.404") 138 | def combined500 = count("sample.routes.combined.500") 139 | def combined503 = count("sample.routes.combined.503") 140 | def combinedTime = timing("sample.routes.combined.time") 141 | 142 | def count(key: String): PartialFunction[String, Unit] = { 143 | case Count(k) if k == key => Unit 144 | } 145 | 146 | def timing(key: String, atLeast: Int = 2): PartialFunction[String, Unit] = { 147 | case Timing(k, time) if k == key => time must beGreaterThanOrEqualTo(atLeast) 148 | } 149 | 150 | object Timing { 151 | val regex = """([^:]+):([0-9]+)\|ms""".r 152 | def unapply(s: String): Option[(String, Int)] = { 153 | s match { 154 | case regex(key, millis) => Some((key, millis.toInt)) 155 | case _ => None 156 | } 157 | } 158 | } 159 | 160 | val Count = """([^:]+):1\|c""".r 161 | 162 | } 163 | -------------------------------------------------------------------------------- /redis/README.md: -------------------------------------------------------------------------------- 1 | # Redis Plugin 2 | 3 | This plugin provides support for [Redis](http://redis.io/) using the best Java driver [Jedis](https://github.com/xetorthio/jedis) and the corresponding Scala wrapper [Sedis](https://github.com/pk11/sedis). Also implements play's internal [Caching] (https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/cache/Cache.scala#L9) interface 4 | 5 | # Features 6 | 7 | ### Provides a Redis-based Cache API (supported types: String, Int, Long, Boolean and Serializable) ie. 8 | 9 | ```java 10 | //java 11 | String f = (String) play.cache.Cache.get("mykey"); 12 | ``` 13 | 14 | and 15 | 16 | ```scala 17 | //scala 18 | val o = play.api.cache.Cache.getAs[String]("mykey") 19 | ``` 20 | 21 | #### Configurable 22 | 23 | * Point to your Redis server using configuration settings ```redis.host```, ```redis.port```, ```redis.password``` and ```redis.database``` (defaults: ```localhost```, ```6379```, ```null``` and ```0```) 24 | * Alternatively, specify a URI-based configuration using ```redis.uri``` (for example: ```redis.uri="redis://user:password@localhost:6379"```). 25 | * Set the timeout in milliseconds using ```redis.timeout``` (default is 2000). 26 | * Configure any aspect of the connection pool. See [the documentation for commons-pool2 ```GenericObjectPoolConfig```](https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/GenericObjectPoolConfig.html), the underlying pool implementation, for more information on each setting. 27 | * redis.pool.maxIdle 28 | * redis.pool.minIdle 29 | * redis.pool.maxTotal 30 | * redis.pool.maxWaitMillis 31 | * redis.pool.testOnBorrow 32 | * redis.pool.testOnReturn 33 | * redis.pool.testWhileIdle 34 | * redis.pool.timeBetweenEvictionRunsMillis 35 | * redis.pool.numTestsPerEvictionRun 36 | * redis.pool.minEvictableIdleTimeMillis 37 | * redis.pool.softMinEvictableIdleTimeMillis 38 | * redis.pool.lifo 39 | * redis.pool.blockWhenExhausted 40 | 41 | 42 | #### Allows direct access to Jedis and Sedis: 43 | play = 2.3.x: 44 | ```java 45 | //java 46 | import com.typesafe.plugin.RedisPlugin; 47 | import redis.clients.jedis.*; 48 | 49 | Jedis j = play.Play.application().plugin(RedisPlugin.class).jedisPool().getResource(); 50 | 51 | try { 52 | /// ... do stuff here 53 | j.set("foo", "bar"); 54 | } finally { 55 | play.Play.application().plugin(RedisPlugin.class).jedisPool().returnResource(j); 56 | } 57 | ``` 58 | 59 | ```scala 60 | //scala 61 | import play.api.Play.current 62 | import com.typesafe.plugin.use 63 | import com.typesafe.plugin.RedisPlugin 64 | 65 | val pool = use[RedisPlugin].sedisPool 66 | pool.withJedisClient { client => 67 | Option[String] single = Dress.up(client).get("single") 68 | } 69 | ``` 70 | play = 2.4.x and 2.5.x: 71 | Because the underlying Sedis Pool was injected for the cache module to use, you can just inject the sedis Pool yourself, something like this: 72 | 73 | ```scala 74 | //scala 75 | import javax.inject.Inject 76 | import org.sedis.Pool 77 | 78 | class TryIt @Inject()(sedisPool: Pool) extends Controller { 79 | val directValue: String = sedisPool.withJedisClient(client => client.get("someKey")) 80 | } 81 | ``` 82 | 83 | ```java 84 | //java 85 | import javax.inject.Inject 86 | import redis.clients.jedis.JedisPool 87 | 88 | class TryIt extends Controller { 89 | 90 | //The JedisPool will be injected for you from the module 91 | @Inject JedisPool jedisPool; 92 | 93 | ... 94 | } 95 | ``` 96 | 97 | The Play 2.4.x and 2.5.x module also supports compile time DI via RedisCacheComponents. Mix this in with your custom application loader just like you would if you were using EhCacheComponents from the reference cache module. 98 | 99 | 100 | 101 | # How to install 102 | 103 | * add 104 | 105 | play < 2.3.x: 106 | ```"com.typesafe" %% "play-plugins-redis" % "2.0.4"``` to your dependencies 107 | 108 | play = 2.3.x: 109 | ```"com.typesafe.play.plugins" %% "play-plugins-redis" % "2.3.1"``` to your dependencies 110 | 111 | * create a file called ```play.plugins``` in your ```conf``` directory 112 | 113 | * add ```550:com.typesafe.plugin.RedisPlugin``` 114 | 115 | * while this plugin is going to be loaded before the default cache implementation, it's a good practice to disable the overwritten plugin: 116 | 117 | ``` 118 | #conf/application.conf 119 | ehcacheplugin=disabled 120 | ``` 121 | 122 | play = 2.4.x: 123 | ```"com.typesafe.play.modules" %% "play-modules-redis" % "2.4.1"``` to your dependencies 124 | and you'll probably need to add this resolver too to resolve Sedis (see [issue](https://github.com/typesafehub/play-plugins/issues/141)): 125 | ```resolvers += "google-sedis-fix" at "http://pk11-scratch.googlecode.com/svn/trunk"``` 126 | * The default cache module (EhCache) will be used for all non-named cache UNLESS this module (RedisModule) is the only cache module that was loaded. If this module is the only cache module being loaded, it will work as expected on named and non-named cache. To disable the default cache module so that this Redis Module can be the default cache you must put this in your configuration: 127 | 128 | ``` 129 | play.modules.disabled = ["play.api.cache.EhCacheModule"] 130 | ``` 131 | 132 | * This module supports play 2.4 NamedCaches through key namespacing on a single Sedis pool. To add additional namepsaces besides the default (play), the configuration would look like such: 133 | 134 | ``` 135 | play.cache.redis.bindCaches = ["db-cache", "user-cache", "session-cache"] 136 | ``` 137 | 138 | play = 2.5.x: 139 | ```"com.typesafe.play.modules" %% "play-modules-redis" % "2.5.0"``` to your dependencies 140 | and you'll probably need to add this resolver too to resolve Sedis (see [issue](https://github.com/typesafehub/play-plugins/issues/141)): 141 | ```resolvers += "google-sedis-fix" at "http://pk11-scratch.googlecode.com/svn/trunk"``` 142 | * The default cache module (EhCache) will be used for all non-named cache UNLESS this module (RedisModule) is the only cache module that was loaded. If this module is the only cache module being loaded, it will work as expected on named and non-named cache. To disable the default cache module so that this Redis Module can be the default cache you must put this in your configuration: 143 | 144 | ``` 145 | play.modules.disabled = ["play.api.cache.EhCacheModule"] 146 | ``` 147 | 148 | * This module supports play 2.5 NamedCaches through key namespacing on a single Sedis pool. To add additional namepsaces besides the default (play), the configuration would look like such: 149 | 150 | ``` 151 | play.cache.redis.bindCaches = ["db-cache", "user-cache", "session-cache"] 152 | ``` 153 | 154 | 155 | 156 | 157 | ## Licence 158 | 159 | This software is licensed under the Apache 2 license, quoted below. 160 | 161 | Copyright 2012 Typesafe (http://www.typesafe.com). 162 | 163 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 164 | 165 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 166 | -------------------------------------------------------------------------------- /dust/sample/public/javascripts/dust-core-0.6.0.min.js: -------------------------------------------------------------------------------- 1 | // 2 | // Dust - Asynchronous Templating v0.6.0 3 | // http://akdubya.github.com/dustjs 4 | // 5 | // Copyright (c) 2010, Aleksander Williams 6 | // Released under the MIT License. 7 | // 8 | var dust={};function getGlobal(){return function(){return this.dust}.call(null)} 9 | (function(f){function h(a,c,d){this.stack=a;this.global=c;this.blocks=d}function k(a,c,d,b){this.tail=c;this.isObject=!f.isArray(a)&&a&&"object"===typeof a;this.head=a;this.index=d;this.of=b}function l(a){this.head=new b(this);this.callback=a;this.out=""}function e(){this.head=new b(this)}function b(a,c,d){this.root=a;this.next=c;this.data="";this.flushable=!1;this.taps=d}function i(a,c){this.head=a;this.tail=c}f.cache={};f.register=function(a,c){a&&(f.cache[a]=c)};f.render=function(a,c,d){d=(new l(d)).head; 10 | f.load(a,d,h.wrap(c)).end()};f.stream=function(a,c){var d=new e;f.nextTick(function(){f.load(a,d.head,h.wrap(c)).end()});return d};f.renderSource=function(a,c,d){return f.compileFn(a)(c,d)};f.compileFn=function(a,c){var d=f.loadSource(f.compile(a,c));return function(a,c){var b=c?new l(c):new e;f.nextTick(function(){d(b.head,h.wrap(a)).end()});return b}};f.load=function(a,c,d){var b=f.cache[a];return b?b(c,d):f.onLoad?c.map(function(c){f.onLoad(a,function(b,e){if(b)return c.setError(b);f.cache[a]|| 11 | f.loadSource(f.compile(e,a));f.cache[a](c,d).end()})}):c.setError(Error("Template Not Found: "+a))};f.loadSource=function(a){return eval(a)};f.isArray=Array.isArray?Array.isArray:function(a){return"[object Array]"==Object.prototype.toString.call(a)};f.nextTick=function(){return"undefined"!==typeof process?process.nextTick:function(a){setTimeout(a,0)}}();f.isEmpty=function(a){return f.isArray(a)&&!a.length?!0:0===a?!1:!a};f.filter=function(a,c,d){if(d)for(var b=0,e=d.length;b\"\']/),j=/&/g,m=//g,o=/\"/g,p=/\'/g;f.escapeHtml=function(a){return"string"===typeof a?!g.test(a)?a:a.replace(j,"&").replace(m,"<").replace(n,">").replace(o,""").replace(p,"'"):a};var q= 20 | /\\/g,r=/\r/g,s=/\u2028/g,t=/\u2029/g,u=/\n/g,v=/\f/g,w=/'/g,x=/"/g,y=/\t/g;f.escapeJs=function(a){return"string"===typeof a?a.replace(q,"\\\\").replace(x,'\\"').replace(w,"\\'").replace(r,"\\r").replace(s,"\\u2028").replace(t,"\\u2029").replace(u,"\\n").replace(v,"\\f").replace(y,"\\t"):a}})(dust);"undefined"!==typeof exports&&(dust.helpers=require("./dust-helpers").helpers,"undefined"!==typeof process&&require("./server")(dust),module.exports=dust); 21 | (function(f){function h(e){e=e.current();return typeof e==="object"&&e.isSelect===true}function k(e,b,f,g,j){var g=g||{},m;if(g.key)m=b.get(g.key);else if(h(b)){m=b.current().value;b.current().isResolved&&(j=function(){return false})}else throw"No key specified for filter and no key found in context from select statement";if(j(g.value,l(m,g.type,b))){if(h(b))b.current().isResolved=true;return e.render(f.block,b)}return f["else"]?e.render(f["else"],b):e.write("")}function l(e,b,f){if(e)switch(b||typeof e){case "number":return+e; 22 | case "string":return""+e;case "boolean":return Boolean(e);case "date":return new Date(e);case "context":return f.get(e)}return e}f.helpers={sep:function(e,b,f){return b.stack.index===b.stack.of-1?e:f.block(e,b)},idx:function(e,b,f){return f.block(e,b.push(b.stack.index))},"if":function(e,b,f,g){if(g&&g.cond){var j=g.cond;if(typeof j==="function"){j="";e.tap(function(b){j=j+b;return""}).render(g.cond,b).untap();j===""&&(j=false)}if(eval(j))return e.render(f.block,b);if(f["else"])return e.render(f["else"], 23 | b)}else typeof window!=="undefined"&&window.console&&window.console.log("No expression given!");return e},select:function(e,b,f,g){return e.render(f.block,b.push({isSelect:true,isResolved:false,value:b.get(g.key)}))},eq:function(e,b,f,g){return k(e,b,f,g,function(b,e){return e===b})},lt:function(e,b,f,g){return k(e,b,f,g,function(b,e){return eb})},gte:function(e,b, 24 | f,g){return k(e,b,f,g,function(b,e){return e>=b})},"else":function(e,b,f,g){return k(e,b,f,g,function(){return true})}}})("undefined"!==typeof exports?exports:getGlobal()); -------------------------------------------------------------------------------- /redis/src/test/scala/com/typesafe/play/redis/RedisCacheApiSpec.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.play.redis 2 | 3 | import java.util.concurrent.atomic.AtomicInteger 4 | import javax.inject.Inject 5 | 6 | import org.sedis.Pool 7 | import org.specs2.specification.AfterAll 8 | import play.api.ApplicationLoader.Context 9 | import play.api._ 10 | import play.api.cache.{CacheApi, Cached} 11 | import play.api.inject.BindingKey 12 | import play.api.inject.guice.GuiceApplicationBuilder 13 | import play.api.mvc.{Action, Results} 14 | import play.api.routing.Router 15 | import play.api.routing.sird._ 16 | import play.api.test._ 17 | import play.cache.{NamedCache, NamedCacheImpl} 18 | 19 | class RedisCachedSpec extends PlaySpecification with AfterAll { 20 | 21 | sequential 22 | 23 | val redisOnlyCache: (() => Application) = { () => new GuiceApplicationBuilder() 24 | .configure(Configuration("play.modules.disabled" -> Seq("play.api.cache.EhCacheModule"))) 25 | .build() 26 | } 27 | 28 | val multiCache: (() => Application) = { () => new GuiceApplicationBuilder() 29 | .configure(Configuration("play.cache.redis.bindCaches" -> Seq("redis-test", "redis-results"), 30 | "play.cache.bindCaches" -> Seq("ehcache-test"))) 31 | .build() 32 | } 33 | 34 | "The cached action" should { 35 | "cache values using injected Redis CachedApi" in new WithApplication(redisOnlyCache()) { 36 | val controller = app.injector.instanceOf[CachedController] 37 | 38 | val result1 = controller.action(FakeRequest()).run 39 | contentAsString(result1) must_== "1" 40 | controller.invoked.get() must_== 1 41 | 42 | val result2 = controller.action(FakeRequest()).run 43 | contentAsString(result2) must_== "1" 44 | controller.invoked.get() must_== 1 45 | 46 | // Test that the same headers are added 47 | header(ETAG, result2) must_== header(ETAG, result1) 48 | header(EXPIRES, result2) must_== header(EXPIRES, result1) 49 | } 50 | 51 | "cache values using named injected Redis CachedApi" in new WithApplication(multiCache()) { 52 | val controller = app.injector.instanceOf[NamedCacheController] 53 | 54 | val result1 = controller.action(FakeRequest()).run 55 | contentAsString(result1) must_== "1" 56 | controller.invoked.get() must_== 1 57 | val result2 = controller.action(FakeRequest()).run 58 | contentAsString(result2) must_== "1" 59 | controller.invoked.get() must_== 1 60 | 61 | // Test that the same headers are added 62 | header(ETAG, result2) must_== header(ETAG, result1) 63 | header(EXPIRES, result2) must_== header(EXPIRES, result1) 64 | 65 | // Test that the values are in the right cache 66 | app.injector.instanceOf[CacheApi].get("foo") must beNone 67 | controller.isCached("foo-etag") must beTrue 68 | } 69 | 70 | "support compile time DI" in new WithApplicationLoader(applicationLoader = new CompileTimeLoader) { 71 | val result1 = route(app, FakeRequest(GET, "/compileTime")).get 72 | status(result1) must_== OK 73 | contentAsString(result1) must_== "1" 74 | 75 | val result2 = route(app, FakeRequest(GET, "/compileTime")).get 76 | status(result2) must_== OK 77 | contentAsString(result2) must_== "1" 78 | 79 | // Test that the same headers are added 80 | header(ETAG, result2) must_== header(ETAG, result1) 81 | header(EXPIRES, result2) must_== header(EXPIRES, result1) 82 | } 83 | } 84 | 85 | "RedisModule" should { 86 | "assume default cache if reference impl is disabled" in new WithApplication(redisOnlyCache()) { 87 | val defaultCache = app.injector.instanceOf[CacheApi] 88 | defaultCache.set("default-foo", "bar") 89 | defaultCache.get("default-foo") must beSome("bar") 90 | } 91 | 92 | "support binding multiple different caches (namespaces) and cache implementations" in new WithApplication(multiCache()) { 93 | val defaultCache = app.injector.instanceOf[CacheApi] 94 | val namedEhCache = app.injector.instanceOf(BindingKey(classOf[CacheApi]).qualifiedWith(new NamedCacheImpl("ehcache-test"))) 95 | val redisCache1 = app.injector.instanceOf(BindingKey(classOf[CacheApi]).qualifiedWith(new NamedCacheImpl("redis-test"))) 96 | val redisCache2 = app.injector.instanceOf(BindingKey(classOf[CacheApi]).qualifiedWith(new NamedCacheImpl("redis-results"))) 97 | 98 | defaultCache.set("default-foo", "bar") 99 | namedEhCache.get("default-foo") must beNone 100 | redisCache1.get("default-foo") must beNone 101 | redisCache2.get("default-foo") must beNone 102 | defaultCache.get("default-foo") must beSome("bar") 103 | 104 | namedEhCache.set("eh-foo", "zzzz") 105 | defaultCache.get("eh-foo") must beNone 106 | redisCache1.get("eh-foo") must beNone 107 | redisCache2.get("eh-foo") must beNone 108 | namedEhCache.get("eh-foo") must beSome("zzzz") 109 | 110 | redisCache1.set("cache1-foo", "buzz") 111 | defaultCache.get("cache1-foo") must beNone 112 | namedEhCache.get("cache1-foo") must beNone 113 | redisCache2.get("cache1-foo") must beNone 114 | redisCache1.get("cache1-foo") must beSome("buzz") 115 | 116 | redisCache2.set("cache2-foo", "zing") 117 | defaultCache.get("cache2-foo") must beNone 118 | namedEhCache.get("cache2-foo") must beNone 119 | redisCache1.get("cache2-foo") must beNone 120 | redisCache2.get("cache2-foo") must beSome("zing") 121 | } 122 | } 123 | 124 | "RedisCacheApi" should { 125 | "support object caching" in new WithApplication(redisOnlyCache()) { 126 | val cache = app.injector.instanceOf[CacheApi] 127 | 128 | val obj = ObjectTest(Seq("test")) 129 | cache.set("object-test", obj) 130 | cache.get("object-test") must beSome(obj) 131 | } 132 | 133 | "support string caching" in new WithApplication(redisOnlyCache()) { 134 | val cache = app.injector.instanceOf[CacheApi] 135 | 136 | val testValue = "my-string" 137 | cache.set("string-test", testValue) 138 | cache.get("string-test") must beSome(testValue) 139 | } 140 | 141 | "support int primitive caching" in new WithApplication(redisOnlyCache()) { 142 | val cache = app.injector.instanceOf[CacheApi] 143 | 144 | val testValue = 5 145 | cache.set("int-test", testValue) 146 | cache.get("int-test") must beSome(testValue) 147 | } 148 | 149 | "support long primitive caching" in new WithApplication(redisOnlyCache()) { 150 | val cache = app.injector.instanceOf[CacheApi] 151 | 152 | val testValue = 5L 153 | cache.set("long-test", testValue) 154 | cache.get("long-test") must beSome(testValue) 155 | } 156 | 157 | "support boolean primitive caching" in new WithApplication(redisOnlyCache()) { 158 | val cache = app.injector.instanceOf[CacheApi] 159 | 160 | val testValue = true 161 | cache.set("bool-test", testValue) 162 | cache.get("bool-test") must beSome(testValue) 163 | } 164 | 165 | "getOrElse should behave same as get and set" in new WithApplication(redisOnlyCache()) { 166 | val cache = app.injector.instanceOf[CacheApi] 167 | 168 | val testValue = "my sample String" 169 | 170 | val orElse = cache.getOrElse[String]("getOrElseTest"){ 171 | testValue 172 | } 173 | orElse mustEqual testValue 174 | 175 | val getValue = cache.get("getOrElseTest") 176 | getValue must beSome(testValue) 177 | 178 | 179 | val setValue = "mySetValue" 180 | cache.set("getOrElseTestSet", setValue) 181 | 182 | cache.getOrElse("getOrElseTestSet"){"anotherStringWhichShouldNotHappen"} mustEqual setValue 183 | 184 | } 185 | 186 | } 187 | 188 | override def afterAll() = { 189 | redisOnlyCache().injector.instanceOf[Pool].withJedisClient(client => client.flushAll()) 190 | } 191 | } 192 | 193 | case class ObjectTest(values: Seq[String]) extends Serializable 194 | 195 | class SomeComponent @Inject()(@NamedCache("redis-test") cache: CacheApi) { 196 | def get(key: String) = cache.get[String](key) 197 | 198 | def set(key: String, value: String) = cache.set(key, value) 199 | } 200 | 201 | class CachedController @Inject()(cached: Cached) { 202 | val invoked = new AtomicInteger() 203 | val action = cached(_ => "foo")(Action(Results.Ok("" + invoked.incrementAndGet()))) 204 | } 205 | 206 | class NamedCacheController @Inject()(@NamedCache("redis-results") cached: Cached, @NamedCache("redis-results") cache: CacheApi) extends CachedController(cached) { 207 | def isCached(key: String): Boolean = cache.get[String](key).isDefined 208 | } 209 | 210 | class CompileTimeLoader extends ApplicationLoader { 211 | override def load(context: Context): Application = { 212 | new CompileTimeAppComponents(context).application 213 | } 214 | } 215 | 216 | class CompileTimeAppComponents(context: Context) extends BuiltInComponentsFromContext(context) with RedisCacheComponents { 217 | 218 | lazy val cached: Cached = new Cached(redisDefaultCacheApi) 219 | 220 | override def router: Router = Router.from { 221 | case GET(p"/compileTime") => cached("redis-ci-cached") { 222 | Action { 223 | val invoked = new AtomicInteger() 224 | Results.Ok("" + invoked.incrementAndGet()) 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /inject/src/main/java/com/google/inject/mini/MiniGuice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.inject.mini; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.lang.reflect.Constructor; 20 | import java.lang.reflect.Field; 21 | import java.lang.reflect.InvocationTargetException; 22 | import java.lang.reflect.Member; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.ParameterizedType; 25 | import java.lang.reflect.Type; 26 | import java.util.ArrayDeque; 27 | import java.util.ArrayList; 28 | import java.util.Arrays; 29 | import java.util.HashMap; 30 | import java.util.HashSet; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Queue; 34 | import java.util.Set; 35 | 36 | import javax.inject.Provider; 37 | 38 | /** 39 | * Proof of concept. A tiny injector suitable for tiny applications. 40 | * 41 | * @author jessewilson@google.com (Jesse Wilson) 42 | * @since 3.0 43 | */ 44 | public final class MiniGuice { 45 | private static final Object UNINITIALIZED = new Object(); 46 | private MiniGuice() {} 47 | 48 | private final Map> bindings = new HashMap>(); 49 | private final Queue requiredKeys = new ArrayDeque(); 50 | private final Set singletons = new HashSet(); 51 | 52 | /** 53 | * Creates an injector defined by {@code modules} and immediately uses it to 54 | * create an instance of {@code type}. The modules can be of any type, and 55 | * must contain {@code @Provides} methods. 56 | * 57 | *

The following injection features are supported: 58 | *

    59 | *
  • Field injection. A class may have any number of field injections, and 60 | * fields may be of any visibility. Static fields will be injected each 61 | * time an instance is injected. 62 | *
  • Constructor injection. A class may have a single {@code 63 | * @Inject}-annotated constructor. Classes that have fields injected 64 | * may omit the {@link @Inject} annotation if they have a public 65 | * no-arguments constructor. 66 | *
  • Injection of {@code @Provides} method parameters. 67 | *
  • {@code @Provides} methods annotated {@code @Singleton}. 68 | *
  • Constructor-injected classes annotated {@code @Singleton}. 69 | *
  • Injection of {@link Provider}s. 70 | *
  • Binding annotations on injected parameters and fields. 71 | *
  • Guice annotations. 72 | *
  • JSR 330 annotations. 73 | *
  • Eager loading of singletons. 74 | *
75 | * 76 | *

Note that method injection is not supported. 77 | */ 78 | public static T inject(Class type, boolean fallbackToDefaultConstructor, Object... modules) { 79 | Key key = new Key(type, null); 80 | MiniGuice miniGuice = new MiniGuice(); 81 | for (Object module : modules) { 82 | miniGuice.install(module); 83 | } 84 | miniGuice.requireKey(key, "root injection"); 85 | miniGuice.addJitBindings(fallbackToDefaultConstructor); 86 | miniGuice.addProviderBindings(); 87 | miniGuice.eagerlyLoadSingletons(); 88 | Provider provider = miniGuice.bindings.get(key); 89 | return type.cast(provider.get()); 90 | } 91 | 92 | private void addProviderBindings() { 93 | Map> providerBindings = new HashMap>(); 94 | for (final Map.Entry> binding : bindings.entrySet()) { 95 | Key key = binding.getKey(); 96 | final Provider value = binding.getValue(); 97 | Provider> providerProvider = new Provider>() { 98 | public Provider get() { 99 | return value; 100 | } 101 | }; 102 | providerBindings.put(new Key(new ProviderType(javax.inject.Provider.class, key.type), 103 | key.annotation), providerProvider); 104 | } 105 | bindings.putAll(providerBindings); 106 | } 107 | 108 | private void requireKey(Key key, Object requiredBy) { 109 | if (key.type instanceof ParameterizedType 110 | && (((ParameterizedType) key.type).getRawType() == Provider.class 111 | || ((ParameterizedType) key.type).getRawType() == javax.inject.Provider.class)) { 112 | Type type = ((ParameterizedType) key.type).getActualTypeArguments()[0]; 113 | key = new Key(type, key.annotation); 114 | } 115 | 116 | requiredKeys.add(new RequiredKey(key, requiredBy)); 117 | } 118 | 119 | private void eagerlyLoadSingletons() { 120 | for (Key key : singletons) { 121 | Provider provider = bindings.get(key); 122 | final Object onlyInstance = provider.get(); 123 | bindings.put(key, new Provider() { 124 | public Object get() { 125 | return onlyInstance; 126 | } 127 | }); 128 | } 129 | } 130 | 131 | public void install(Object module) { 132 | boolean hasProvidesMethods = false; 133 | for (Class c = module.getClass(); c != Object.class; c = c.getSuperclass()) { 134 | for (Method method : c.getDeclaredMethods()) { 135 | if (method.getAnnotation(com.google.inject.Provides.class) != null) { 136 | Key key = key(method, method.getGenericReturnType(), method.getAnnotations()); 137 | addProviderMethodBinding(key, module, method); 138 | hasProvidesMethods = true; 139 | } 140 | } 141 | } 142 | if (!hasProvidesMethods) { 143 | throw new IllegalArgumentException("No @Provides methods on " + module); 144 | } 145 | } 146 | 147 | private void addProviderMethodBinding(Key key, final Object instance, final Method method) { 148 | final Key[] parameterKeys = parametersToKeys( 149 | method, method.getGenericParameterTypes(), method.getParameterAnnotations()); 150 | method.setAccessible(true); 151 | final Provider unscoped = new Provider() { 152 | public Object get() { 153 | Object[] parameters = keysToValues(parameterKeys); 154 | try { 155 | return method.invoke(instance, parameters); 156 | } catch (IllegalAccessException e) { 157 | throw new RuntimeException(e); 158 | } catch (InvocationTargetException e) { 159 | throw new RuntimeException(e.getCause()); 160 | } 161 | } 162 | }; 163 | 164 | boolean singleton = method.getAnnotation(javax.inject.Singleton.class) != null; 165 | putBinding(key, unscoped, singleton); 166 | } 167 | 168 | private void addJitBindings(boolean fallbackToDefaultConstructor) { 169 | RequiredKey requiredKey; 170 | while ((requiredKey = requiredKeys.poll()) != null) { 171 | Key key = requiredKey.key; 172 | if (bindings.containsKey(key)) { 173 | continue; 174 | } 175 | if (!(key.type instanceof Class) || key.annotation != null) { 176 | throw new IllegalArgumentException("No binding for " + key); 177 | } 178 | addJitBinding(key, requiredKey.requiredBy, fallbackToDefaultConstructor); 179 | } 180 | } 181 | 182 | private void addJitBinding(Key key, Object requiredBy, boolean fallbackToDefaultConstructor) { 183 | Class type = (Class) key.type; 184 | 185 | /* 186 | * Lookup the injectable fields and their corresponding keys. 187 | */ 188 | final List injectedFields = new ArrayList(); 189 | List fieldKeysList = new ArrayList(); 190 | for (Class c = type; c != Object.class; c = c.getSuperclass()) { 191 | for (Field field : c.getDeclaredFields()) { 192 | if (field.getAnnotation(javax.inject.Inject.class) == null) { 193 | continue; 194 | } 195 | field.setAccessible(true); 196 | injectedFields.add(field); 197 | Key fieldKey = key(field, field.getGenericType(), field.getAnnotations()); 198 | fieldKeysList.add(fieldKey); 199 | requireKey(fieldKey, field); 200 | } 201 | } 202 | 203 | /* 204 | * Lookup @Inject-annotated constructors. If there's no @Inject-annotated 205 | * constructor, use a default constructor if the class has other injections. 206 | */ 207 | 208 | final Key[] fieldKeys = fieldKeysList.toArray(new Key[fieldKeysList.size()]); 209 | Constructor injectedConstructor = null; 210 | for (Constructor constructor : type.getDeclaredConstructors()) { 211 | if (constructor.getAnnotation(javax.inject.Inject.class) == null) { 212 | continue; 213 | } 214 | if (injectedConstructor != null) { 215 | throw new IllegalArgumentException("Too many injectable constructors on " + type); 216 | } 217 | constructor.setAccessible(true); 218 | injectedConstructor = constructor; 219 | } 220 | if (injectedConstructor == null) { 221 | if (fieldKeys.length == 0) { 222 | throw new IllegalArgumentException("No injectable constructor on " 223 | + type + " required by " + requiredBy); 224 | } 225 | try { 226 | injectedConstructor = type.getConstructor(); 227 | } catch (NoSuchMethodException e) { 228 | if (fallbackToDefaultConstructor) { 229 | try { 230 | Constructor[] constructors = type.getConstructors(); 231 | for (Constructor c: constructors) { 232 | if (c.getTypeParameters().length == 0) { 233 | injectedConstructor = c; 234 | injectedConstructor.setAccessible(true); 235 | break; 236 | } 237 | } 238 | if (injectedConstructor == null) throw new NoSuchMethodException(); 239 | System.out.println("YAY"); 240 | } catch (NoSuchMethodException ex) { 241 | throw new IllegalArgumentException("Could not find default constructor " 242 | + type + " required by " + requiredBy +" available constructors:"+type.getConstructors().length ); 243 | } 244 | } else 245 | throw new IllegalArgumentException("No injectable constructor on " 246 | + type + " required by " + requiredBy); 247 | } 248 | } 249 | /* 250 | * Create a provider that invokes the constructor and sets its fields. 251 | */ 252 | final Constructor constructor = injectedConstructor; 253 | final Key[] parameterKeys = parametersToKeys( 254 | constructor, constructor.getGenericParameterTypes(), constructor.getParameterAnnotations()); 255 | final Provider unscoped = new Provider() { 256 | public Object get() { 257 | Object[] constructorParameters = keysToValues(parameterKeys); 258 | try { 259 | Object result = constructor.newInstance(constructorParameters); 260 | Object[] fieldValues = keysToValues(fieldKeys); 261 | for (int i = 0; i < fieldValues.length; i++) { 262 | injectedFields.get(i).set(result, fieldValues[i]); 263 | } 264 | return result; 265 | } catch (IllegalAccessException e) { 266 | throw new RuntimeException(e.getCause()); 267 | } catch (InvocationTargetException e) { 268 | throw new RuntimeException(e.getCause()); 269 | } catch (InstantiationException e) { 270 | throw new RuntimeException(e); 271 | } 272 | } 273 | }; 274 | 275 | boolean singleton = type.getAnnotation(javax.inject.Singleton.class) != null; 276 | putBinding(new Key(type, null), unscoped, singleton); 277 | } 278 | 279 | private void putBinding(Key key, Provider provider, boolean singleton) { 280 | if (singleton) { 281 | singletons.add(key); 282 | final Provider unscoped = provider; 283 | provider = new Provider() { 284 | private Object onlyInstance = UNINITIALIZED; 285 | public Object get() { 286 | if (onlyInstance == UNINITIALIZED) { 287 | onlyInstance = unscoped.get(); 288 | } 289 | return onlyInstance; 290 | } 291 | }; 292 | } 293 | 294 | if (bindings.put(key, provider) != null) { 295 | throw new IllegalArgumentException("Duplicate binding " + key); 296 | } 297 | } 298 | 299 | private Object[] keysToValues(Key[] parameterKeys) { 300 | Object[] parameters = new Object[parameterKeys.length]; 301 | for (int i = 0; i < parameterKeys.length; i++) { 302 | parameters[i] = bindings.get(parameterKeys[i]).get(); 303 | } 304 | return parameters; 305 | } 306 | 307 | private Key[] parametersToKeys(Member member, Type[] types, Annotation[][] annotations) { 308 | final Key[] parameterKeys = new Key[types.length]; 309 | for (int i = 0; i < parameterKeys.length; i++) { 310 | String name = member + " parameter " + i; 311 | parameterKeys[i] = key(name, types[i], annotations[i]); 312 | requireKey(parameterKeys[i], name); 313 | } 314 | return parameterKeys; 315 | } 316 | 317 | public Key key(Object subject, Type type, Annotation[] annotations) { 318 | Annotation bindingAnnotation = null; 319 | for (Annotation a : annotations) { 320 | if (a.annotationType().getAnnotation(javax.inject.Qualifier.class) == null) { 321 | continue; 322 | } 323 | if (bindingAnnotation != null) { 324 | throw new IllegalArgumentException("Too many binding annotations on " + subject); 325 | } 326 | bindingAnnotation = a; 327 | } 328 | return new Key(type, bindingAnnotation); 329 | } 330 | 331 | private static boolean equal(Object a, Object b) { 332 | return a == null ? b == null : a.equals(b); 333 | } 334 | 335 | private static final class Key { 336 | final Type type; 337 | final Annotation annotation; 338 | 339 | Key(Type type, Annotation annotation) { 340 | this.type = type; 341 | this.annotation = annotation; 342 | } 343 | 344 | @Override public boolean equals(Object o) { 345 | return o instanceof Key 346 | && ((Key) o).type.equals(type) 347 | && equal(annotation, ((Key) o).annotation); 348 | } 349 | 350 | @Override public int hashCode() { 351 | int result = type.hashCode(); 352 | if (annotation != null) { 353 | result += (37 * annotation.hashCode()); 354 | } 355 | return result; 356 | } 357 | 358 | @Override public String toString() { 359 | return "key[type=" + type + ",annotation=" + annotation + "]"; 360 | } 361 | } 362 | 363 | private class RequiredKey { 364 | private final Key key; 365 | private final Object requiredBy; 366 | 367 | private RequiredKey(Key key, Object requiredBy) { 368 | this.key = key; 369 | this.requiredBy = requiredBy; 370 | } 371 | } 372 | 373 | private static final class ProviderType implements ParameterizedType { 374 | private final Class rawType; 375 | private final Type typeArgument; 376 | 377 | public ProviderType(Class rawType, Type typeArgument) { 378 | this.rawType = rawType; 379 | this.typeArgument = typeArgument; 380 | } 381 | 382 | public Type getRawType() { 383 | return rawType; 384 | } 385 | 386 | public Type[] getActualTypeArguments() { 387 | return new Type[] { typeArgument }; 388 | } 389 | 390 | public Type getOwnerType() { 391 | return null; 392 | } 393 | 394 | @Override public boolean equals(Object o) { 395 | if (o instanceof ParameterizedType) { 396 | ParameterizedType that = (ParameterizedType) o; 397 | return Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments()) 398 | && that.getRawType() == rawType; 399 | } 400 | return false; 401 | } 402 | 403 | @Override public int hashCode() { 404 | return Arrays.hashCode(getActualTypeArguments()) ^ rawType.hashCode(); 405 | } 406 | } 407 | } 408 | 409 | -------------------------------------------------------------------------------- /dust/sample/public/javascripts/dust-core-0.6.0.js: -------------------------------------------------------------------------------- 1 | // 2 | // Dust - Asynchronous Templating v0.6.0 3 | // http://akdubya.github.com/dustjs 4 | // 5 | // Copyright (c) 2010, Aleksander Williams 6 | // Released under the MIT License. 7 | // 8 | 9 | var dust = {}; 10 | 11 | function getGlobal(){ 12 | return (function(){ 13 | return this.dust; 14 | }).call(null); 15 | } 16 | 17 | (function(dust) { 18 | 19 | dust.cache = {}; 20 | 21 | dust.register = function(name, tmpl) { 22 | if (!name) return; 23 | dust.cache[name] = tmpl; 24 | }; 25 | 26 | dust.render = function(name, context, callback) { 27 | var chunk = new Stub(callback).head; 28 | dust.load(name, chunk, Context.wrap(context)).end(); 29 | }; 30 | 31 | dust.stream = function(name, context) { 32 | var stream = new Stream(); 33 | dust.nextTick(function() { 34 | dust.load(name, stream.head, Context.wrap(context)).end(); 35 | }); 36 | return stream; 37 | }; 38 | 39 | dust.renderSource = function(source, context, callback) { 40 | return dust.compileFn(source)(context, callback); 41 | }; 42 | 43 | dust.compileFn = function(source, name) { 44 | var tmpl = dust.loadSource(dust.compile(source, name)); 45 | return function(context, callback) { 46 | var master = callback ? new Stub(callback) : new Stream(); 47 | dust.nextTick(function() { 48 | tmpl(master.head, Context.wrap(context)).end(); 49 | }); 50 | return master; 51 | } 52 | }; 53 | 54 | dust.load = function(name, chunk, context) { 55 | var tmpl = dust.cache[name]; 56 | if (tmpl) { 57 | return tmpl(chunk, context); 58 | } else { 59 | if (dust.onLoad) { 60 | return chunk.map(function(chunk) { 61 | dust.onLoad(name, function(err, src) { 62 | if (err) return chunk.setError(err); 63 | if (!dust.cache[name]) dust.loadSource(dust.compile(src, name)); 64 | dust.cache[name](chunk, context).end(); 65 | }); 66 | }); 67 | } 68 | return chunk.setError(new Error("Template Not Found: " + name)); 69 | } 70 | }; 71 | 72 | dust.loadSource = function(source, path) { 73 | return eval(source); 74 | }; 75 | 76 | if (Array.isArray) { 77 | dust.isArray = Array.isArray; 78 | } else { 79 | dust.isArray = function(arr) { 80 | return Object.prototype.toString.call(arr) == "[object Array]"; 81 | }; 82 | } 83 | 84 | dust.nextTick = (function() { 85 | if (typeof process !== "undefined") { 86 | return process.nextTick; 87 | } else { 88 | return function(callback) { 89 | setTimeout(callback,0); 90 | } 91 | } 92 | } )(); 93 | 94 | dust.isEmpty = function(value) { 95 | if (dust.isArray(value) && !value.length) return true; 96 | if (value === 0) return false; 97 | return (!value); 98 | }; 99 | 100 | dust.filter = function(string, auto, filters) { 101 | if (filters) { 102 | for (var i=0, len=filters.length; i\"\']/), 491 | AMP = /&/g, 492 | LT = //g, 494 | QUOT = /\"/g, 495 | SQUOT = /\'/g; 496 | 497 | dust.escapeHtml = function(s) { 498 | if (typeof s === "string") { 499 | if (!HCHARS.test(s)) { 500 | return s; 501 | } 502 | return s.replace(AMP,'&').replace(LT,'<').replace(GT,'>').replace(QUOT,'"').replace(SQUOT, '''); 503 | } 504 | return s; 505 | }; 506 | 507 | var BS = /\\/g, 508 | CR = /\r/g, 509 | LS = /\u2028/g, 510 | PS = /\u2029/g, 511 | NL = /\n/g, 512 | LF = /\f/g, 513 | SQ = /'/g, 514 | DQ = /"/g, 515 | TB = /\t/g; 516 | 517 | dust.escapeJs = function(s) { 518 | if (typeof s === "string") { 519 | return s 520 | .replace(BS, '\\\\') 521 | .replace(DQ, '\\"') 522 | .replace(SQ, "\\'") 523 | .replace(CR, '\\r') 524 | .replace(LS, '\\u2028') 525 | .replace(PS, '\\u2029') 526 | .replace(NL, '\\n') 527 | .replace(LF, '\\f') 528 | .replace(TB, "\\t"); 529 | } 530 | return s; 531 | }; 532 | 533 | })(dust); 534 | 535 | if (typeof exports !== "undefined") { 536 | dust.helpers = require("./dust-helpers").helpers; 537 | if (typeof process !== "undefined") { 538 | require('./server')(dust); 539 | } 540 | module.exports = dust; 541 | } 542 | (function(dust){ 543 | 544 | function isSelect(context) { 545 | var value = context.current(); 546 | return typeof value === "object" && value.isSelect === true; 547 | } 548 | 549 | function filter(chunk, context, bodies, params, filter) { 550 | var params = params || {}, 551 | actual, expected; 552 | 553 | if (params.key) { 554 | actual = context.get(params.key); 555 | } else if (isSelect(context)) { 556 | actual = context.current().value; 557 | if (context.current().isResolved) { 558 | filter = function() { return false; }; 559 | } 560 | } else { 561 | throw "No key specified for filter and no key found in context from select statement"; 562 | } 563 | 564 | expected = params.value; 565 | if (filter(expected, coerce(actual, params.type, context))) { 566 | if (isSelect(context)) { 567 | context.current().isResolved = true; 568 | } 569 | return chunk.render(bodies.block, context); 570 | } else if (bodies['else']) { 571 | return chunk.render(bodies['else'], context); 572 | } 573 | 574 | return chunk.write(''); 575 | } 576 | 577 | function coerce (value, type, context) { 578 | if (value) { 579 | switch (type || typeof(value)) { 580 | case 'number': return +value; 581 | case 'string': return String(value); 582 | case 'boolean': return Boolean(value); 583 | case 'date': return new Date(value); 584 | case 'context': return context.get(value); 585 | } 586 | } 587 | 588 | return value; 589 | } 590 | 591 | var helpers = { 592 | 593 | sep: function(chunk, context, bodies) { 594 | if (context.stack.index === context.stack.of - 1) { 595 | return chunk; 596 | } 597 | return bodies.block(chunk, context); 598 | }, 599 | 600 | idx: function(chunk, context, bodies) { 601 | return bodies.block(chunk, context.push(context.stack.index)); 602 | }, 603 | 604 | "if": function( chunk, context, bodies, params ){ 605 | if( params && params.cond ){ 606 | var cond = params.cond; 607 | 608 | // resolve dust references in the expression 609 | if( typeof cond === "function" ){ 610 | cond = ''; 611 | chunk.tap( function( data ){ 612 | cond += data; 613 | return ''; 614 | } ).render( params.cond, context ).untap(); 615 | if( cond === '' ){ 616 | cond = false; 617 | } 618 | } 619 | // eval expressions with no dust references 620 | if( eval( cond ) ){ 621 | return chunk.render( bodies.block, context ); 622 | } 623 | if( bodies['else'] ){ 624 | return chunk.render( bodies['else'], context ); 625 | } 626 | } 627 | // no condition 628 | else { 629 | if( typeof window !== 'undefined' && window.console ){ 630 | window.console.log( "No expression given!" ); 631 | } 632 | } 633 | return chunk; 634 | }, 635 | 636 | select: function(chunk, context, bodies, params) { 637 | return chunk.render(bodies.block, context.push({ isSelect: true, isResolved: false, value: context.get(params.key) })); 638 | }, 639 | 640 | eq: function(chunk, context, bodies, params) { 641 | return filter(chunk, context, bodies, params, function(expected, actual) { return actual === expected; }); 642 | }, 643 | 644 | lt: function(chunk, context, bodies, params) { 645 | return filter(chunk, context, bodies, params, function(expected, actual) { return actual < expected; }); 646 | }, 647 | 648 | lte: function(chunk, context, bodies, params) { 649 | return filter(chunk, context, bodies, params, function(expected, actual) { return actual <= expected; }); 650 | }, 651 | 652 | gt: function(chunk, context, bodies, params) { 653 | return filter(chunk, context, bodies, params, function(expected, actual) { return actual > expected; }); 654 | }, 655 | 656 | gte: function(chunk, context, bodies, params) { 657 | return filter(chunk, context, bodies, params, function(expected, actual) { return actual >= expected; }); 658 | }, 659 | 660 | "else": function(chunk, context, bodies, params) { 661 | return filter(chunk, context, bodies, params, function(expected, actual) { return true; }); 662 | } 663 | }; 664 | 665 | dust.helpers = helpers; 666 | 667 | })(typeof exports !== 'undefined' ? exports : getGlobal()); --------------------------------------------------------------------------------