Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: revbingo/play
...
head fork: revbingo/play
  • 20 commits
  • 30 files changed
  • 0 commit comments
  • 9 contributors
Commits on Jul 09, 2011
@hadashi hadashi Restore classloader once done with Play.init() to play nicely in a se…
…rvlet container
b90a677
Commits on Aug 17, 2011
@tms tms [#908] Added configuration option to control client authentication wh…
…en using SSL

Added documentation for play.netty.clientAuth
d306310
@tkral tkral [#1041] Allow SASL authentication for memcached 2451d7d
@pepite pepite [#603] Fix matching host in routes f877fec
@grandfatha grandfatha [#351] @OnApplicationStop for Jobs 1efc5e5
@pepite pepite Merge pull request #208 from adamil/read-ivy-config
[#807] Read default ivy config
47910ef
Commits on Aug 18, 2011
Peter Hilton [#713] Docs: document multiple required validation limitation 93be431
Peter Hilton Docs: added JPA.setRollbackOnly() clarification from source comment ff1155c
Arthur Handle sigterm gracefully 1e73b71
@pepite pepite [#807] Added doc 66573cc
@pepite pepite [#351] @OnApplicationStop for Jobs 82fe752
@pepite pepite Extra doc for e-mail explaining the default mock up behaviour in dev 04ea6df
Commits on Aug 19, 2011
@guillaumebort guillaumebort Allow simpler configuration URL for PostgreSQL, and adapt the MySQL o…
…ne to support same URL pattern (the old syntax for MySQL is still compatible)
c0df25e
Commits on Aug 20, 2011
@guillaumebort guillaumebort Fix regex for MySQL and PostgreSQL connection URL 6d7bdf0
@guillaumebort guillaumebort Allow to specify additional JVM arguments using JAVA_OPTS environment…
… variable.
bfb715e
Commits on Aug 24, 2011
@guillaumebort guillaumebort Update Groovy to 1.7.10 43cbfde
@guillaumebort guillaumebort Fix security issues 711eb01
Commits on Aug 25, 2011
@guillaumebort guillaumebort Merge pull request #288 from hadashi/1.2.x
Play nicely with servlet containers class loader.
c9b803b
@pepite pepite [#1064] + test 0fcc72b
Peter Hilton Docs: removed new sections on multiple databases, accidentally merged…
… from master
170cf6d
Showing with 242 additions and 84 deletions.
  1. +9 −0 documentation/manual/configuration.textile
  2. +21 −1 documentation/manual/dependency.textile
  3. +6 −0 documentation/manual/emails.textile
  4. +17 −1 documentation/manual/jobs.textile
  5. +1 −17 documentation/manual/jpa.textile
  6. +0 −21 documentation/manual/model.textile
  7. +1 −1  documentation/manual/validation.textile
  8. +2 −2 framework/dependencies.yml
  9. BIN  framework/lib/{groovy-all-1.7.7.jar → groovy-all-1.7.10.jar}
  10. BIN  framework/lib/memcached-2.4.2.jar
  11. BIN  framework/lib/memcached-2.6.jar
  12. +2 −0  framework/pym/play/application.py
  13. +13 −1 framework/pym/play/commands/base.py
  14. +4 −7 framework/src/play/CorePlugin.java
  15. +1 −1  framework/src/play/Play.java
  16. +32 −3 framework/src/play/cache/MemcachedImpl.java
  17. +9 −2 framework/src/play/data/FileUpload.java
  18. +2 −1  framework/src/play/data/parsing/ApacheMultipartParser.java
  19. +19 −2 framework/src/play/db/DBPlugin.java
  20. +12 −2 framework/src/play/deps/DependenciesManager.java
  21. +29 −0 framework/src/play/jobs/JobsPlugin.java
  22. +16 −0 framework/src/play/jobs/OnApplicationStop.java
  23. +4 −0 framework/src/play/libs/Files.java
  24. +13 −15 framework/src/play/mvc/Router.java
  25. +3 −0  framework/src/play/server/ServletWrapper.java
  26. +8 −2 framework/src/play/server/ssl/SslHttpServerPipelineFactory.java
  27. +8 −1 resources/application-skel/conf/application.conf
  28. +1 −1  samples-and-tests/just-test-cases/app/controllers/Application.java
  29. +1 −0  samples-and-tests/just-test-cases/conf/routes
  30. +8 −3 samples-and-tests/just-test-cases/test/routing.test.html
View
9 documentation/manual/configuration.textile
@@ -836,6 +836,15 @@ Values - ‘standard names’ from the JDK Security API:
Default: @JKS@
+bc. play.netty.clientAuth
+
+Values
+
+* @want@ Controls whether accepted server-mode SSLSockets will be initially configured to request client authentication.
+* @need@ Controls whether accepted server-mode SSLSockets will be initially configured to require client authentication.
+* @none@ None of the above
+
+Default: @none@
h2(#upload). File upload
View
22 documentation/manual/dependency.textile
@@ -303,6 +303,26 @@ repositories:
Note: don't forget to run @play dependencies myplayapp@.
+
+h3. Custom ivy settings
+
+Play is using Ivy under the hood. If you require a special configuration such as setting a proxy, basic authentication for an internal maven nexus repository, you can edit the ivysettings.xml file. It is located in the .ivy2 folder in your home directory.
+
+bc. Example1, you want ivy to ignore checksums:
+# .ivy2/ivysettings.xml
+<ivysettings>
+ <property name="ivy.checksums" value=""/>
+</ivysettings>
+
+bc. Example2, you want to use basic auth:
+# .ivy2/ivysettings.xml
+<ivysettings>
+ <credentials host="maven-repo.xxx" realm="Sonatype Nexus Repository Manager" username="user" passwd="reallygreatpassword"/>
+</ivysettings>
+
+
+There are many things you can configure see: http://ant.apache.org/ivy/history/2.1.0/settings.html
+
p(note). **Continuing the discussion**
-Next: %(next)"Database evolutions":evolutions%.
+Next: %(next)"Database evolutions":evolutions%.
View
6 documentation/manual/emails.textile
@@ -133,6 +133,12 @@ Two additional configuration properties let you override default behaviour:
* "mail.smtp.port":configuration#mail.smtp.port
+By default, in DEV mode, the e-mail will be printed to the console, while in PROD mode it will be sent to the actual SMTP server. You can change the default behaviour in DEV mode by commenting the following line:
+
+bc. # Default is to use a mock Mailer
+mail.smtp=mock
+
+
h3. <a name="gmail">Using Gmail</a>
To use Gmail’s servers, for example when you deploy with "playapps":http://www.playframework.org/modules/playapps, use this configuration:
View
18 documentation/manual/jobs.textile
@@ -115,6 +115,22 @@ bc. public static void encodeVideo(Long videoId) {
Calling @now()@ on a Job returns a @Promise@ value that you can use to retrieve the task result once finished.
+h2. <a name="concepts">Stopping the application</a>
+
+Because you sometimes need to perform some action before the application shutdown, Play also provides a <code>@OnApplicationStop</code> annotation.
+
+bc. import play.jobs.*;
+
+@OnApplicationStop
+public class Bootstrap extends Job {
+
+ public void doJob() {
+ Fixture.deleteAll();
+ }
+
+}
+
p(note). **Continuing the discussion**
-Let's see how to combine Jobs with more powerfull %(next)"Asynchronous programming with HTTP":asynchronous%.
+Let's see how to combine Jobs with more powerful %(next)"Asynchronous programming with HTTP":asynchronous%.
+
View
18 documentation/manual/jpa.textile
@@ -22,7 +22,7 @@ h2. <a name="transactions">Transaction management</a>
Play will automatically manage transactions for you. It will start a transaction for each HTTP request and commit it when the HTTP response is sent. If your code throws an exception, the transaction will automatically rollback.
-If you need to force transaction rollback from the application code, you can use the @JPA.setRollbackOnly()@ method.
+If you need to force transaction rollback from the application code, you can use the @JPA.setRollbackOnly()@ method, which tells JPA not to commit the current transaction.
You can also use annotations to specify how transactions should be handled.
@@ -278,22 +278,6 @@ bc. for(Post p : Post.<Post>findAll()) {
}
-h2. <a name="multiple">Using JPA with multiple databases</a>
-
-You can configure Play to use multiple databases, as described in the "model":model#multiple section.
-
-When creating Entity and Model classes for databases other than the default, you have to annotate the Class with <code>@PersistenceUnit(name="other")</code>. In this case ‘other’ corresponds to the DB config name ‘other’ (See Model-documentation for more info).
-
-You can use @package-info.java@ and annotate the package with <code>@PersistenceUnit</code> to enforce a specific JPA configuration to all entity-classes in the Java package.
-
-If you need to specify special Hibernate configuration parameters for non-default JPA configurations, you must prefix the @hibernate.@ settings in application.conf with ‘db_configName.’, eg. @db_other.hibernate.some.special.setting=true@.
-
-When executing methods on the Entity classes and objects, Play automatically uses the correct database configuration, but if you like you can access it with code:
-
-bc. EntityManager em = JPA.getJPAConfig("other").em();
-
-@JPA.getJPAConfig(configName)@ returns an object with all the same methods you find in JPA.
-
p(note). It is important to notice that Play does not support XA (two-phase commits). If you use several different JPA configurations in the same request, **Play will try to commit as many transactions as possible**. If the commit to the first database succeeds and the second commit to the other database fails, the first commit will not get rolled back. Bear this in mind when using multiple JPA configurations in the same request.
p(note). **Continuing the discussion**
View
21 documentation/manual/model.textile
@@ -203,27 +203,6 @@ Product product = Product.findById(2L);
product.save();
product.delete();
-h3. <a name="multiple">Support for multiple databases</a>
-
-You can configure Play to use multiple (separate) databases.
-
-The default database is configured by configuration parameters in @conf/application.conf@ whose keys start with ‘db.’ (e.g: @db.url@). To configure an additional database, add an underscore and a suffix to the ‘db’ part of the key, like this:
-
-bc. db_other.url=jdbc:mysql://localhost/test
-db_other.driver=com.mysql.jdbc.Driver
-db_other.user=root
-db_other.pass=
-
-This database configuration is now called 'other' in Play. For example, configure JPA for this 'other' configuration like this:
-
-bc. db_other.jpa.dialect=<dialect>
-
-You can then access this configuration from your application’s Java code like this:
-
-bc. Connection conn = DB.getDBConfig("other").getConnection()
-
-@DB.getDBConfig(configName)@ returns an Object with all the methods you usually find as statics methods in the @play.db.DB@ class.
-
h2. <a name="stateless">Keep the model stateless</a>
Play is designed to operate in a ‘share nothing’ architecture. The idea is to keep the application completely stateless. By doing this you will allow your application to run on as many server nodes as needed at the same time.
View
2  documentation/manual/validation.textile
@@ -84,7 +84,7 @@ This changes the output to:
bc. name is required
age is required
-p(note). *Limitation*: Play cannot determine the correct parameter name when more than required-field validation using the @validation.required(age)@ syntax fails. In this case, you must specify the field name: @validation.required("age", age)@.
+p(note). *Limitation*: Play cannot determine the correct parameter name when more than one required-field validation using the @validation.required(age)@ syntax fails. In this case, you must specify the field name directly, i.e. @validation.required("age", age)@.
This error key defaults to the parameter name, and is itself used to look up a message. For example, the @name@ parameter in the @hello@ action method above could be localised with:
View
4 framework/dependencies.yml
@@ -42,7 +42,7 @@ require: &allDependencies
- org.apache.geronimo.specs -> geronimo-servlet_2.5_spec 1.2
- org.apache.ivy -> ivy 2.2.0
- org.bouncycastle -> bcprov-jdk15 1.45
- - org.codehaus.groovy -> groovy-all 1.7.7
+ - org.codehaus.groovy -> groovy-all 1.7.10
- org.eclipse.jdt.core 3.6.0
- org.hibernate -> hibernate-core 3.6.1.Final
- org.hibernate -> hibernate-commons-annotations 3.2.0.Final
@@ -54,7 +54,7 @@ require: &allDependencies
- org.slf4j -> slf4j-api 1.6.1
- org.slf4j -> slf4j-log4j12 1.6.1
- org.yaml -> snakeyaml 1.7
- - spy -> memcached 2.4.2
+ - spy -> memcached 2.6
- com.thoughtworks.xstream -> xstream 1.3
# Default repositories, used for all projects
View
BIN  framework/lib/groovy-all-1.7.7.jar → framework/lib/groovy-all-1.7.10.jar
Binary file not shown
View
BIN  framework/lib/memcached-2.4.2.jar
Binary file not shown
View
BIN  framework/lib/memcached-2.6.jar
Binary file not shown
View
2  framework/pym/play/application.py
@@ -217,6 +217,8 @@ def java_cmd(self, java_args, cp_args=None, className='play.server.Server', args
memory = self.readConf('jvm.memory')
if memory:
java_args = java_args + memory.split(' ')
+ elif 'JAVA_OPTS' in os.environ:
+ java_args = java_args + os.environ['JAVA_OPTS'].split(' ')
if cp_args is None:
cp_args = self.cp_args()
View
14 framework/pym/play/commands/base.py
@@ -8,6 +8,7 @@
import urllib2
import webbrowser
import time
+import signal
from play.utils import *
@@ -119,14 +120,25 @@ def new(app, args, env, cmdloader=None):
print "~ Have fun!"
print "~"
+process = None
+
+def handle_sigterm(signum, frame):
+ global process
+ if 'process' in globals():
+ process.terminate()
+ sys.exit(0)
+
def run(app, args):
+ global process
app.check()
print "~ Ctrl+C to stop"
print "~ "
java_cmd = app.java_cmd(args)
try:
- subprocess.call(java_cmd, env=os.environ)
+ process = subprocess.Popen (java_cmd, env=os.environ)
+ signal.signal(signal.SIGTERM, handle_sigterm)
+ process.wait()
except OSError:
print "Could not execute the java executable, please make sure the JAVA_HOME environment variable is set properly (the java executable should reside at JAVA_HOME/bin/java). "
sys.exit(-1)
View
11 framework/src/play/CorePlugin.java
@@ -69,6 +69,9 @@ public static String computeApplicationStatus(boolean json) {
/**
* Intercept /@status and check that the Authorization header is valid.
* Then ask each plugin for a status dump and send it over the HTTP response.
+ *
+ * You can ask the /@status using the authorization header and putting your status secret key in it.
+ * Prior to that you would be required to start play with a -DstatusKey=yourkey
*/
@Override
public boolean rawInvocation(Request request, Response response) throws Exception {
@@ -84,7 +87,7 @@ public boolean rawInvocation(Request request, Response response) throws Exceptio
}
response.contentType = request.path.contains(".json") ? "application/json" : "text/plain";
Header authorization = request.headers.get("authorization");
- if (request.isLoopback || (authorization != null && Crypto.sign("@status").equals(authorization.value()))) {
+ if (authorization != null && (Crypto.sign("@status").equals(authorization.value()) || System.getProperty("statusKey", Play.secretKey).equals(authorization.value()))) {
response.print(computeApplicationStatus(request.path.contains(".json")));
response.status = 200;
return true;
@@ -142,12 +145,6 @@ public String getStatus() {
out.println(plugin.index + ":" + plugin.getClass().getName() + " [" + (Play.pluginCollection.isEnabled(plugin) ? "enabled" : "disabled") + "]");
}
out.println();
- out.println("Configuration:");
- out.println("~~~~~~~~~~~~~~");
- for (Object key : Play.configuration.keySet()) {
- out.println(key + "=" + Play.configuration.getProperty(key.toString()));
- }
- out.println();
out.println("Threads:");
out.println("~~~~~~~~");
try {
View
2  framework/src/play/Play.java
@@ -532,8 +532,8 @@ public void run(){
public static synchronized void stop() {
if (started) {
Logger.trace("Stopping the play application");
- started = false;
pluginCollection.onApplicationStop();
+ started = false;
Cache.stop();
Router.lastLoading = 0L;
}
View
35 framework/src/play/cache/MemcachedImpl.java
@@ -6,12 +6,19 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
+import java.net.InetSocketAddress;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+
import net.spy.memcached.AddrUtil;
+import net.spy.memcached.ConnectionFactory;
+import net.spy.memcached.ConnectionFactoryBuilder;
import net.spy.memcached.MemcachedClient;
+import net.spy.memcached.auth.AuthDescriptor;
+import net.spy.memcached.auth.PlainCallbackHandler;
import net.spy.memcached.transcoders.SerializingTranscoder;
import play.Logger;
import play.Play;
@@ -84,8 +91,10 @@ protected Object deserialize(byte[] data) {
public void initClient() throws IOException {
System.setProperty("net.spy.log.LoggerImpl", "net.spy.memcached.compat.log.Log4JLogger");
+
+ List<InetSocketAddress> addrs;
if (Play.configuration.containsKey("memcached.host")) {
- client = new MemcachedClient(AddrUtil.getAddresses(Play.configuration.getProperty("memcached.host")));
+ addrs = AddrUtil.getAddresses(Play.configuration.getProperty("memcached.host"));
} else if (Play.configuration.containsKey("memcached.1.host")) {
int nb = 1;
String addresses = "";
@@ -93,9 +102,29 @@ public void initClient() throws IOException {
addresses += Play.configuration.get("memcached." + nb + ".host") + " ";
nb++;
}
- client = new MemcachedClient(AddrUtil.getAddresses(addresses));
+ addrs = AddrUtil.getAddresses(addresses);
+ } else {
+ throw new ConfigurationException("Bad configuration for memcached: missing host(s)");
+ }
+
+ if (Play.configuration.containsKey("memcached.user")) {
+ String memcacheUser = Play.configuration.getProperty("memcached.user");
+ String memcachePassword = Play.configuration.getProperty("memcached.password");
+ if (memcachePassword == null) {
+ throw new ConfigurationException("Bad configuration for memcached: missing password");
+ }
+
+ // Use plain SASL to connect to memcached
+ AuthDescriptor ad = new AuthDescriptor(new String[]{"PLAIN"},
+ new PlainCallbackHandler(memcacheUser, memcachePassword));
+ ConnectionFactory cf = new ConnectionFactoryBuilder()
+ .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
+ .setAuthDescriptor(ad)
+ .build();
+
+ client = new MemcachedClient(cf, addrs);
} else {
- throw new ConfigurationException(("Bad configuration for memcached"));
+ client = new MemcachedClient(addrs);
}
}
View
11 framework/src/play/data/FileUpload.java
@@ -5,6 +5,9 @@
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import play.Logger;
import play.data.parsing.TempFilePlugin;
import play.exceptions.UnexpectedException;
import play.libs.Files;
@@ -21,9 +24,13 @@ public FileUpload() {
public FileUpload(FileItem fileItem) {
this.fileItem = fileItem;
- defaultFile = new File(TempFilePlugin.createTempFolder(), fileItem.getFieldName() + File.separator + fileItem.getName());
- defaultFile.getParentFile().mkdirs();
+ File tmp = TempFilePlugin.createTempFolder();
+ defaultFile = new File(tmp, FilenameUtils.getName(fileItem.getFieldName()) + File.separator + FilenameUtils.getName(fileItem.getName()));
try {
+ if(!defaultFile.getCanonicalPath().startsWith(tmp.getCanonicalPath())) {
+ throw new IOException("Temp file try to override existing file?");
+ }
+ defaultFile.getParentFile().mkdirs();
fileItem.write(defaultFile);
} catch (Exception e) {
throw new IllegalStateException("Error when trying to write to file " + defaultFile.getAbsolutePath(), e);
View
3  framework/src/play/data/parsing/ApacheMultipartParser.java
@@ -24,6 +24,7 @@
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FileCleaningTracker;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.output.DeferredFileOutputStream;
import play.Logger;
import play.Play;
@@ -142,7 +143,7 @@ public AutoFileItem(FileItemStream stream) {
this.fieldName = stream.getFieldName();
this.contentType = stream.getContentType();
this.isFormField = stream.isFormField();
- this.fileName = stream.getName();
+ this.fileName = FilenameUtils.getName(stream.getName());
this.sizeThreshold = Integer.parseInt(Play.configuration.getProperty("upload.threshold", "10240"));
this.repository = null;
}
View
21 framework/src/play/db/DBPlugin.java
@@ -204,13 +204,30 @@ private static boolean changed() {
p.put("db.destroyMethod", "close");
}
- Matcher m = new jregex.Pattern("^mysql:(({user}[\\w]+)(:({pwd}[^@]+))?@)?({name}[\\w]+)$").matcher(p.getProperty("db", ""));
+ Matcher m = new jregex.Pattern("^mysql:(//)?(({user}[a-zA-Z0-9_]+)(:({pwd}[^@]+))?@)?(({host}[^/]+)/)?({name}[^\\s]+)$").matcher(p.getProperty("db", ""));
if (m.matches()) {
String user = m.group("user");
String password = m.group("pwd");
String name = m.group("name");
+ String host = m.group("host");
p.put("db.driver", "com.mysql.jdbc.Driver");
- p.put("db.url", "jdbc:mysql://localhost/" + name + "?useUnicode=yes&characterEncoding=UTF-8&connectionCollation=utf8_general_ci");
+ p.put("db.url", "jdbc:mysql://" + (host == null ? "localhost" : host) + "/" + name + "?useUnicode=yes&characterEncoding=UTF-8&connectionCollation=utf8_general_ci");
+ if (user != null) {
+ p.put("db.user", user);
+ }
+ if (password != null) {
+ p.put("db.pass", password);
+ }
+ }
+
+ m = new jregex.Pattern("^postgres:(//)?(({user}[a-zA-Z0-9_]+)(:({pwd}[^@]+))?@)?(({host}[^/]+)/)?({name}[^\\s]+)$").matcher(p.getProperty("db", ""));
+ if (m.matches()) {
+ String user = m.group("user");
+ String password = m.group("pwd");
+ String name = m.group("name");
+ String host = m.group("host");
+ p.put("db.driver", "org.postgresql.Driver");
+ p.put("db.url", "jdbc:postgresql://" + (host == null ? "localhost" : host) + "/" + name);
if (user != null) {
p.put("db.user", user);
}
View
14 framework/src/play/deps/DependenciesManager.java
@@ -27,8 +27,9 @@ public static void main(String[] args) throws Exception {
// Paths
File application = new File(System.getProperty("application.path"));
File framework = new File(System.getProperty("framework.path"));
+ File userHome = new File(System.getProperty("user.home"));
- DependenciesManager deps = new DependenciesManager(application, framework);
+ DependenciesManager deps = new DependenciesManager(application, framework, userHome);
ResolveReport report = deps.resolve();
if(report != null) {
@@ -50,11 +51,13 @@ public static void main(String[] args) throws Exception {
File application;
File framework;
+ File userHome;
HumanReadyLogger logger;
- public DependenciesManager(File application, File framework) {
+ public DependenciesManager(File application, File framework, File userHome) {
this.application = application;
this.framework = framework;
+ this.userHome = userHome;
}
public void report() {
@@ -295,6 +298,13 @@ public Ivy configure() throws Exception {
ivySettings.setDefaultConflictManager(conflictManager);
Ivy ivy = Ivy.newInstance(ivySettings);
+
+ // Default ivy config see: http://play.lighthouseapp.com/projects/57987-play-framework/tickets/807
+ File ivyDefaultSettings = new File(userHome, ".ivy2/ivysettings.xml");
+ if(ivyDefaultSettings.exists()) {
+ ivy.configure(ivyDefaultSettings);
+ }
+
if (debug) {
ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_DEBUG));
} else if (verbose) {
View
29 framework/src/play/jobs/JobsPlugin.java
@@ -225,6 +225,35 @@ public void onApplicationStart() {
@Override
public void onApplicationStop() {
+
+ List<Class> jobs = Play.classloader.getAssignableClasses(Job.class);
+
+ for (final Class clazz : jobs) {
+ // @OnApplicationStop
+ if (clazz.isAnnotationPresent(OnApplicationStop.class)) {
+ try {
+ Job<?> job = ((Job<?>) clazz.newInstance());
+ scheduledJobs.add(job);
+ job.run();
+ if (job.wasError) {
+ if (job.lastException != null) {
+ throw job.lastException;
+ }
+ throw new RuntimeException("@OnApplicationStop Job has failed");
+ }
+ } catch (InstantiationException e) {
+ throw new UnexpectedException("Job could not be instantiated", e);
+ } catch (IllegalAccessException e) {
+ throw new UnexpectedException("Job could not be instantiated", e);
+ } catch (Throwable ex) {
+ if (ex instanceof PlayException) {
+ throw (PlayException) ex;
+ }
+ throw new UnexpectedException(ex);
+ }
+ }
+ }
+
executor.shutdownNow();
executor.getQueue().clear();
}
View
16 framework/src/play/jobs/OnApplicationStop.java
@@ -0,0 +1,16 @@
+package play.jobs;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A job run when the application is stopped under graceful circumstances.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface OnApplicationStop {
+
+}
View
4 framework/src/play/libs/Files.java
@@ -89,6 +89,7 @@ public static boolean copyDir(File from, File to) {
public static void unzip(File from, File to) {
try {
+ String outDir = to.getCanonicalPath();
ZipFile zipFile = new ZipFile(from);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
@@ -98,6 +99,9 @@ public static void unzip(File from, File to) {
continue;
}
File f = new File(to, entry.getName());
+ if(!f.getCanonicalPath().startsWith(outDir)) {
+ throw new IOException("Corrupted zip file");
+ }
f.getParentFile().mkdirs();
FileOutputStream os = new FileOutputStream(f);
IO.copy(zipFile.getInputStream(entry), os);
View
28 framework/src/play/mvc/Router.java
@@ -234,9 +234,7 @@ public static void detectChanges(String prefix) {
public static void routeOnlyStatic(Http.Request request) {
for (Route route : routes) {
try {
- String format = request.format;
- String host = request.host;
- if (route.matches(request.method, request.path, format, host) != null) {
+ if (route.matches(request.method, request.path, request.format, request.domain) != null) {
break;
}
} catch (Throwable t) {
@@ -261,9 +259,7 @@ public static Route route(Http.Request request) {
}
}
for (Route route : routes) {
- String format = request.format;
- String host = request.host;
- Map<String, String> args = route.matches(request.method, request.path, format, host);
+ Map<String, String> args = route.matches(request.method, request.path, request.format, request.domain);
if (args != null) {
request.routeArgs = args;
request.action = route.action;
@@ -360,7 +356,9 @@ public static String reverse(VirtualFile file, boolean absolute) {
String base = Http.Request.current() == null ? Play.configuration.getProperty("application.baseUrl", "application.baseUrl") : Http.Request.current().getBase();
if (!StringUtils.isEmpty(route.host)) {
// Compute the host
- to = (isSecure ? "https://" : "http://") + route.host + to;
+ int port = Http.Request.current() == null ? 80 : Http.Request.current().get().port;
+ String host = (port != 80 && port != 443) ? route.host + ":" + port : route.host;
+ to = (isSecure ? "https://" : "http://") + host + to;
} else {
to = base + to;
}
@@ -795,10 +793,10 @@ private boolean contains(String accept) {
* @param method GET/POST/etc.
* @param path Part after domain and before query-string. Starts with a "/".
* @param accept Format, e.g. html.
- * @param host AKA the domain.
+ * @param domain The domain (host without port).
* @return ???
*/
- public Map<String, String> matches(String method, String path, String accept, String host) {
+ public Map<String, String> matches(String method, String path, String accept, String domain) {
// Normalize
if (path.equals(Play.ctxPath)) {
path = path + "/";
@@ -808,10 +806,10 @@ private boolean contains(String accept) {
Matcher matcher = pattern.matcher(path);
- boolean hostMatches = (host == null);
- if (host != null) {
+ boolean hostMatches = (domain == null);
+ if (domain != null) {
- Matcher hostMatcher = hostPattern.matcher(host);
+ Matcher hostMatcher = hostPattern.matcher(domain);
hostMatches = hostMatcher.matches();
}
// Extract the host variable
@@ -845,11 +843,11 @@ private boolean contains(String accept) {
localArgs.put(arg.name, matcher.group(arg.name));
}
}
- if (hostArg != null && host != null) {
+ if (hostArg != null && domain != null) {
// Parse the hostname and get only the part we are interested in
String routeValue = hostArg.defaultValue.replaceAll("\\{.*}", "");
- host = host.replace(routeValue, "");
- localArgs.put(hostArg.name, host);
+ domain = domain.replace(routeValue, "");
+ localArgs.put(hostArg.name, domain);
}
localArgs.putAll(staticArgs);
return localArgs;
View
3  framework/src/play/server/ServletWrapper.java
@@ -62,6 +62,7 @@
private static boolean routerInitializedWithContext = false;
public void contextInitialized(ServletContextEvent e) {
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
String appDir = e.getServletContext().getRealPath("/WEB-INF/application");
File root = new File(appDir);
final String playId = e.getServletContext().getInitParameter("play.id");
@@ -81,6 +82,8 @@ public void contextInitialized(ServletContextEvent e) {
if (isGreaterThan(e.getServletContext(), 2, 4)) {
loadRouter(e.getServletContext().getContextPath());
}
+
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
}
public void contextDestroyed(ServletContextEvent e) {
View
10 framework/src/play/server/ssl/SslHttpServerPipelineFactory.java
@@ -18,14 +18,20 @@
public ChannelPipeline getPipeline() throws Exception {
Integer max = Integer.valueOf(Play.configuration.getProperty("play.netty.maxContentLength", "-1"));
+ String mode = Play.configuration.getProperty("play.netty.clientAuth", "none");
ChannelPipeline pipeline = pipeline();
// Add SSL handler first to encrypt and decrypt everything.
SSLEngine engine = SslHttpServerContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
- engine.setNeedClientAuth(true);
- engine.setWantClientAuth(true);
+
+ if ("want".equalsIgnoreCase(mode)) {
+ engine.setWantClientAuth(true);
+ } else if ("need".equalsIgnoreCase(mode)) {
+ engine.setNeedClientAuth(true);
+ }
+
engine.setEnableSessionCreation(true);
pipeline.addLast("ssl", new SslHandler(engine));
View
9 resources/application-skel/conf/application.conf
@@ -85,7 +85,10 @@ date.format=yyyy-MM-dd
# db=mem
#
# To connect to a local MySQL5 database, use:
-# db=mysql:user:pwd@database_name
+# db=mysql://user:pwd@host/database
+#
+# To connect to a local PostgreSQL9 database, use:
+# db=postgres://user:pwd@host/database
#
# If you need a full JDBC configuration use the following :
# db.url=jdbc:postgresql:database_name
@@ -137,6 +140,10 @@ attachments.path=data/attachments
# Or you can specify multiple host to build a distributed cache
# memcached.1.host=127.0.0.1:11211
# memcached.2.host=127.0.0.1:11212
+#
+# Use plain SASL to authenticate for memcached
+# memcached.user=
+# memcached.password=
# HTTP Response headers control for static files
# ~~~~~
View
2  samples-and-tests/just-test-cases/app/controllers/Application.java
@@ -96,7 +96,7 @@ public static void imagesAssets() {
}
public static void dashboard(String client) {
-
+ renderText(client);
}
public static void hello(String name) {
View
1  samples-and-tests/just-test-cases/conf/routes
@@ -18,6 +18,7 @@ GET /{lucky}/doIt Application.showIt
GET www.zenexity.com/hello Application.helloZen
GET {clientName}.myApp.com/home Application.myHomePage
GET assets.{_}/images Application.imagesAssets
+GET {client}/client Application.dashboard
GET {client}.{_}/dashboard Application.dashboard
POST /ressource/file/{id} Rest.postOrPutFile
View
11 samples-and-tests/just-test-cases/test/routing.test.html
@@ -122,9 +122,9 @@
// Host
open('/')
- assertTextPresent('1: http://static.foo.com/assets/hello.html')
+ assertTextPresent('1: http://static.foo.com:9003/assets/hello.html')
assertTextPresent('2: /assets/hello.html')
- assertTextPresent('3: http://static2.foo.com/x/hello.html')
+ assertTextPresent('3: http://static2.foo.com:9003/x/hello.html')
assertTextPresent('4: /x/hello.html')
assertTextPresent('5: http://localhost:9003/public/image.gif')
assertTextPresent('6: /public/image.gif')
@@ -141,4 +141,9 @@
assertTextPresent('17: http://localhost:9003/application/ok?re=A')
assertTextPresent('18: http://localhost:9003/re/TITI')
assertTextPresent('19: http://assets.localhost:9003/images')
-#{/selenium}
+
+
+ open('/client')
+ assertTextPresent('localhost')
+
+#{/selenium}

No commit comments for this range

Something went wrong with that request. Please try again.