play.Play Maven / Gradle / Ivy
The newest version!
package play;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import play.cache.Cache;
import play.classloading.ApplicationClasses;
import play.classloading.ApplicationClassloader;
import play.deps.DependenciesManager;
import play.exceptions.PlayException;
import play.exceptions.RestartNeededException;
import play.exceptions.UnexpectedException;
import play.libs.IO;
import play.mvc.Http;
import play.mvc.Router;
import play.plugins.PluginCollection;
import play.templates.TemplateLoader;
import play.utils.OrderSafeProperties;
import play.vfs.VirtualFile;
/**
* Main framework class
*/
public class Play {
/**
* 2 modes
*/
public enum Mode {
/**
* Enable development-specific features, e.g. view the documentation at the URL {@literal "/@documentation"}.
*/
DEV,
/**
* Disable development-specific features.
*/
PROD;
public boolean isDev() {
return this == DEV;
}
public boolean isProd() {
return this == PROD;
}
}
/**
* Is the application initialized
*/
public static boolean initialized = false;
/**
* Is the application started
*/
public static boolean started = false;
/**
* True when the one and only shutdown hook is enabled
*/
private static boolean shutdownHookEnabled = false;
/**
* The framework ID
*/
public static String id = System.getProperty("play.id", "");
/**
* The application mode
*/
public static Mode mode = Mode.DEV;
/**
* The application root
*/
public static File applicationPath = new File(System.getProperty("application.path", "."));
/**
* tmp dir
*/
public static File tmpDir = null;
/**
* tmp dir is readOnly
*/
public static boolean readOnlyTmp = false;
/**
* The framework root
*/
public static File frameworkPath = null;
/**
* All loaded application classes
*/
public static ApplicationClasses classes;
/**
* The application classLoader
*/
public static ApplicationClassloader classloader;
/**
* All paths to search for files
*/
public static List roots = new ArrayList<>(16);
/**
* All paths to search for Java files
*/
public static List javaPath = new CopyOnWriteArrayList<>();
/**
* All paths to search for templates files
*/
public static List templatesPath = new ArrayList<>(2);
/**
* Main routes file
*/
public static VirtualFile routes;
/**
* Plugin routes files
*/
public static Map modulesRoutes = new HashMap<>(16);
/**
* The loaded configuration files
*/
public static Set confs = new HashSet<>(1);
/**
* The app configuration (already resolved from the framework id)
*/
public static Properties configuration = new Properties();
/**
* The last time than the application has started
*/
public static long startedAt;
/**
* The list of supported locales
*/
public static List langs = new ArrayList<>(16);
/**
* The very secret key
*/
public static String secretKey;
/**
* pluginCollection that holds all loaded plugins and all enabled plugins..
*/
public static PluginCollection pluginCollection = new PluginCollection();
/**
* Readonly list containing currently enabled plugins. This list is updated from pluginCollection when
* pluginCollection is modified Play plugins Use pluginCollection instead.
*/
@Deprecated
public static List plugins = pluginCollection.getEnabledPlugins();
/**
* Modules
*/
public static Map modules = new HashMap<>(16);
/**
* Framework version
*/
public static String version = null;
/**
* Context path (when several application are deployed on the same host)
*/
public static String ctxPath = "";
static boolean firstStart = true;
public static boolean usePrecompiled = false;
public static boolean forceProd = false;
/**
* Lazy load the templates on demand
*/
public static boolean lazyLoadTemplates = false;
/**
* This is used as default encoding everywhere related to the web: request, response, WS
*/
public static String defaultWebEncoding = "utf-8";
/**
* This flag indicates if the app is running in a standalone Play server or as a WAR in an applicationServer
*/
public static boolean standalonePlayServer = true;
/**
* Init the framework
*
* @param root
* The application path
* @param id
* The framework id to use
*/
public static void init(File root, String id) {
// Simple things
Play.id = id;
Play.started = false;
Play.applicationPath = root;
// load all play.static of exists
initStaticStuff();
guessFrameworkPath();
// Read the configuration file
readConfiguration();
Play.classes = new ApplicationClasses();
// Configure logs
Logger.init();
String logLevel = configuration.getProperty("application.log", "INFO");
// only override log-level if Logger was not configured manually
if (!Logger.configuredManually) {
Logger.setUp(logLevel);
}
Logger.recordCaller = Boolean.parseBoolean(configuration.getProperty("application.log.recordCaller", "false"));
Logger.info("Starting %s", root.getAbsolutePath());
if (configuration.getProperty("play.tmp", "tmp").equals("none")) {
tmpDir = null;
Logger.debug("No tmp folder will be used (play.tmp is set to none)");
} else {
tmpDir = new File(configuration.getProperty("play.tmp", "tmp"));
if (!tmpDir.isAbsolute()) {
tmpDir = new File(applicationPath, tmpDir.getPath());
}
if (Logger.isTraceEnabled()) {
Logger.trace("Using %s as tmp dir", Play.tmpDir);
}
if (!tmpDir.exists()) {
try {
if (readOnlyTmp) {
throw new Exception("ReadOnly tmp");
}
tmpDir.mkdirs();
} catch (Throwable e) {
tmpDir = null;
Logger.warn("No tmp folder will be used (cannot create the tmp dir), caused by: %s", e);
}
}
}
// Mode
try {
mode = Mode.valueOf(configuration.getProperty("application.mode", "DEV").toUpperCase());
} catch (IllegalArgumentException e) {
Logger.error("Illegal mode '%s', use either prod or dev", configuration.getProperty("application.mode"));
fatalServerErrorOccurred();
}
// Force the Production mode if forceProd or precompile is activate
// Set to the Prod mode must be done before loadModules call
// as some modules (e.g. DocViewer) is only available in DEV
if (usePrecompiled || forceProd || System.getProperty("precompile") != null) {
mode = Mode.PROD;
}
// Context path
ctxPath = configuration.getProperty("http.path", ctxPath);
// Build basic java source path
VirtualFile appRoot = VirtualFile.open(applicationPath);
roots.clear();
roots.add(appRoot);
javaPath.clear();
javaPath.add(appRoot.child("app"));
javaPath.add(appRoot.child("conf"));
// Build basic templates path
templatesPath.clear();
if (appRoot.child("app/views").exists() || (usePrecompiled && appRoot.child("precompiled/templates/app/views").exists())) {
templatesPath.add(appRoot.child("app/views"));
}
// Main route file
routes = appRoot.child("conf/routes");
// Plugin route files
modulesRoutes.clear();
// Load modules
loadModules(appRoot);
// Load the templates from the framework after the one from the modules
templatesPath.add(VirtualFile.open(new File(frameworkPath, "framework/templates")));
// Enable a first classloader
classloader = new ApplicationClassloader();
// Fix ctxPath
if ("/".equals(Play.ctxPath)) {
Play.ctxPath = "";
}
// Default cookie domain
Http.Cookie.defaultDomain = configuration.getProperty("application.defaultCookieDomain", null);
if (Http.Cookie.defaultDomain != null) {
Logger.info("Using default cookie domain: " + Http.Cookie.defaultDomain);
}
// Plugins
pluginCollection.loadPlugins();
// Done !
if (mode == Mode.PROD) {
if (preCompile() && System.getProperty("precompile") == null) {
start();
} else {
return;
}
} else {
Logger.warn("You're running Play! in DEV mode");
}
// Plugins
pluginCollection.onApplicationReady();
Play.initialized = true;
}
public static void guessFrameworkPath() {
// Guess the framework path
try {
URL versionUrl = Play.class.getResource("/play/version");
// Read the content of the file
Play.version = new LineNumberReader(new InputStreamReader(versionUrl.openStream())).readLine();
// This is used only by the embedded server (Mina, Netty, Jetty etc)
URI uri = new URI(versionUrl.toString().replace(" ", "%20"));
if (frameworkPath == null || !frameworkPath.exists()) {
if (uri.getScheme().equals("jar")) {
String jarPath = uri.getSchemeSpecificPart().substring(5, uri.getSchemeSpecificPart().lastIndexOf("!"));
frameworkPath = new File(jarPath).getParentFile().getParentFile().getAbsoluteFile();
} else if (uri.getScheme().equals("file")) {
frameworkPath = new File(uri).getParentFile().getParentFile().getParentFile().getParentFile();
} else {
throw new UnexpectedException(
"Cannot find the Play! framework - trying with uri: " + uri + " scheme " + uri.getScheme());
}
}
} catch (Exception e) {
throw new UnexpectedException("Where is the framework ?", e);
}
}
/**
* Read application.conf and resolve overridden key using the play id mechanism.
*/
public static void readConfiguration() {
confs = new HashSet<>();
configuration = readOneConfigurationFile("application.conf");
extractHttpPort();
// Plugins
pluginCollection.onConfigurationRead();
}
private static void extractHttpPort() {
String javaCommand = System.getProperty("sun.java.command", "");
jregex.Matcher m = new jregex.Pattern(".* --http.port=({port}\\d+)").matcher(javaCommand);
if (m.matches()) {
configuration.setProperty("http.port", m.group("port"));
}
}
private static Properties readOneConfigurationFile(String filename) {
Properties propsFromFile = null;
VirtualFile appRoot = VirtualFile.open(applicationPath);
VirtualFile conf = appRoot.child("conf/" + filename);
if (confs.contains(conf)) {
throw new RuntimeException("Detected recursive @include usage. Have seen the file " + filename + " before");
}
try {
propsFromFile = IO.readUtf8Properties(conf.inputstream());
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
Logger.fatal("Cannot read " + filename);
fatalServerErrorOccurred();
}
}
confs.add(conf);
// OK, check for instance specifics configuration
Properties newConfiguration = new OrderSafeProperties();
Pattern pattern = Pattern.compile("^%([a-zA-Z0-9_\\-]+)\\.(.*)$");
for (Object key : propsFromFile.keySet()) {
Matcher matcher = pattern.matcher(key + "");
if (!matcher.matches()) {
newConfiguration.put(key, propsFromFile.get(key).toString().trim());
}
}
for (Object key : propsFromFile.keySet()) {
Matcher matcher = pattern.matcher(key + "");
if (matcher.matches()) {
String instance = matcher.group(1);
if (instance.equals(id)) {
newConfiguration.put(matcher.group(2), propsFromFile.get(key).toString().trim());
}
}
}
propsFromFile = newConfiguration;
// Resolve ${..}
pattern = Pattern.compile("\\$\\{([^}]+)}");
for (Object key : propsFromFile.keySet()) {
String value = propsFromFile.getProperty(key.toString());
Matcher matcher = pattern.matcher(value);
StringBuffer newValue = new StringBuffer(100);
while (matcher.find()) {
String jp = matcher.group(1);
String r;
if (jp.equals("application.path")) {
r = Play.applicationPath.getAbsolutePath();
} else if (jp.equals("play.path")) {
r = Play.frameworkPath.getAbsolutePath();
} else {
r = System.getProperty(jp);
if (r == null) {
r = System.getenv(jp);
}
if (r == null) {
Logger.warn("Cannot replace %s in configuration (%s=%s)", jp, key, value);
continue;
}
}
matcher.appendReplacement(newValue, r.replaceAll("\\\\", "\\\\\\\\"));
}
matcher.appendTail(newValue);
propsFromFile.setProperty(key.toString(), newValue.toString());
}
// Include
Map