Java tutorial
/* * Copyright (c) 2009-2016, b3log.org & hacpai.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ package org.b3log.latke; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URL; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Enumeration; import java.util.Locale; import java.util.Properties; import java.util.TimeZone; import javax.servlet.ServletContext; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.b3log.latke.cron.CronService; import org.b3log.latke.ioc.Lifecycle; import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Logger; import org.b3log.latke.repository.jdbc.util.Connections; import org.b3log.latke.servlet.AbstractServletListener; import org.b3log.latke.thread.local.LocalThreadService; import org.b3log.latke.util.Strings; import org.b3log.latke.util.freemarker.Templates; /** * Latke framework configuration utility facade. * * @author <a href="http://88250.b3log.org">Liang Ding</a> * @version 2.6.8.13, Sep 18, 2016 * @see #initRuntimeEnv() * @see #shutdown() * @see #getServePath() * @see #getStaticServePath() */ public final class Latkes { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(Latkes.class.getName()); /** * Locale. Initializes this by {@link #setLocale(Locale)}. */ private static Locale locale; /** * Where Latke runs on?. */ private static RuntimeEnv runtimeEnv; /** * Which mode Latke runs in? */ private static RuntimeMode runtimeMode; /** * Local properties (local.properties). */ private static final Properties LOCAL_PROPS = new Properties(); /** * Application startup time millisecond. */ private static String startupTimeMillis = String.valueOf(System.currentTimeMillis()); /** * Static resource version. */ private static String staticResourceVersion; /** * Server scheme. */ private static String serverScheme; /** * Static server scheme. */ private static String staticServerScheme; /** * Server host. */ private static String serverHost; /** * Static server host. */ private static String staticServerHost; /** * Server port. */ private static String serverPort; /** * Static server port. */ private static String staticServerPort; /** * Server. (${serverScheme}://${serverHost}:${serverPort}) */ private static String server; /** * Serve path. (${server}${contextPath}) */ private static String servePath; /** * Static server. (${staticServerScheme}://${staticServerHost}:${staticServerPort}) */ private static String staticServer; /** * Static serve path. (${staticServer}${staticPath}) */ private static String staticServePath; /** * Context path. */ private static String contextPath; /** * Static path. */ private static String staticPath; /** * IoC scan path. */ private static String scanPath; /** * Latke configurations (latke.properties). */ private static final Properties LATKE_PROPS = new Properties(); /** * Latke remote interfaces configurations (remote.properties). */ private static final Properties REMOTE_PROPS = new Properties(); /** * H2 database TCP server. * * <p> * If Latke is running on {@link RuntimeEnv#LOCAL LOCAL} environment and using {@link RuntimeDatabase#H2 H2} * database and specified newTCPServer=true in local.properties, creates a H2 TCP server and starts it. * </p> */ private static org.h2.tools.Server h2; static { LOGGER.debug("Loading latke.properties"); try { final InputStream resourceAsStream = Latkes.class.getResourceAsStream("/latke.properties"); if (null != resourceAsStream) { LATKE_PROPS.load(resourceAsStream); LOGGER.debug("Loaded latke.properties"); } } catch (final Exception e) { LOGGER.log(Level.ERROR, "Not found latke.properties"); throw new RuntimeException("Not found latke.properties"); } LOGGER.debug("Loading local.properties"); try { final InputStream resourceAsStream = Latkes.class.getResourceAsStream("/local.properties"); if (null != resourceAsStream) { LOCAL_PROPS.load(resourceAsStream); LOGGER.debug("Loaded local.properties"); } } catch (final Exception e) { LOGGER.log(Level.DEBUG, "Not found local.properties"); // Ignored } LOGGER.debug("Loading remote.properties"); try { final InputStream resourceAsStream = Latkes.class.getResourceAsStream("/remote.properties"); if (null != resourceAsStream) { REMOTE_PROPS.load(resourceAsStream); LOGGER.debug("Loaded remote.properties"); } } catch (final Exception e) { LOGGER.log(Level.DEBUG, "Not found Latke remote.properties"); // Ignored } } /** * Gets static resource (JS, CSS files) version. * * <p> * Returns the value of "staticResourceVersion" property in local.properties. Returns the * {@link #startupTimeMillis application startup millisecond} if not found the "staticResourceVersion" property in * local.properties. * </p> * * @return static resource version */ public static String getStaticResourceVersion() { if (null == staticResourceVersion) { staticResourceVersion = LATKE_PROPS.getProperty("staticResourceVersion"); if (null == staticResourceVersion) { staticResourceVersion = startupTimeMillis; } } return staticResourceVersion; } /** * Gets server scheme. * * <p> * Returns the value of "serverScheme" property in latke.properties. * </p> * * @return server scheme */ public static String getServerScheme() { if (null == serverScheme) { serverScheme = LATKE_PROPS.getProperty("serverScheme"); if (null == serverScheme) { throw new IllegalStateException("latke.properties [serverScheme] is empty"); } } return serverScheme; } /** * Gets server host. * * <p> * Returns the value of "serverHost" property in latke.properties. * </p> * * @return server host */ public static String getServerHost() { if (null == serverHost) { serverHost = LATKE_PROPS.getProperty("serverHost"); if (null == serverHost) { throw new IllegalStateException("latke.properties [serverHost] is empty"); } } return serverHost; } /** * Gets server port. * * <p> * Returns the value of "serverPort" property in latke.properties. * </p> * * @return server port */ public static String getServerPort() { if (null == serverPort) { serverPort = LATKE_PROPS.getProperty("serverPort"); } return serverPort; } /** * Gets server. * * @return server, ${serverScheme}://${serverHost}:${serverPort} */ public static String getServer() { if (null == server) { final StringBuilder serverBuilder = new StringBuilder(getServerScheme()).append("://") .append(getServerHost()); final String port = getServerPort(); if (!Strings.isEmptyOrNull(port) && !port.equals("80")) { serverBuilder.append(':').append(port); } server = serverBuilder.toString(); } return server; } /** * Gets serve path. * * @return serve path, ${server}${contextPath} */ public static String getServePath() { if (null == servePath) { servePath = getServer() + getContextPath(); } return servePath; } /** * Gets static server scheme. * * <p> * Returns the value of "staticServerScheme" property in latke.properties, returns the value of "serverScheme" if * not found. * </p> * * @return static server scheme */ public static String getStaticServerScheme() { if (null == staticServerScheme) { staticServerScheme = LATKE_PROPS.getProperty("staticServerScheme"); if (null == staticServerScheme) { staticServerScheme = getServerScheme(); } } return staticServerScheme; } /** * Gets static server host. * * <p> * Returns the value of "staticServerHost" property in latke.properties, returns the value of "serverHost" if not * found. * </p> * * @return static server host */ public static String getStaticServerHost() { if (null == staticServerHost) { staticServerHost = LATKE_PROPS.getProperty("staticServerHost"); if (null == staticServerHost) { staticServerHost = getServerHost(); } } return staticServerHost; } /** * Gets static server port. * * <p> * Returns the value of "staticServerPort" property in latke.properties, returns the value of "serverPort" if not * found. * </p> * * @return static server port */ public static String getStaticServerPort() { if (null == staticServerPort) { staticServerPort = LATKE_PROPS.getProperty("staticServerPort"); if (null == staticServerPort) { staticServerPort = getServerPort(); } } return staticServerPort; } /** * Gets static server. * * @return static server, ${staticServerScheme}://${staticServerHost}:${staticServerPort} */ public static String getStaticServer() { if (null == staticServer) { final StringBuilder staticServerBuilder = new StringBuilder(getStaticServerScheme()).append("://") .append(getStaticServerHost()); final String port = getStaticServerPort(); if (!Strings.isEmptyOrNull(port) && !port.equals("80")) { staticServerBuilder.append(':').append(port); } staticServer = staticServerBuilder.toString(); } return staticServer; } /** * Gets static serve path. * * @return static serve path, ${staticServer}${staticPath} */ public static String getStaticServePath() { if (null == staticServePath) { staticServePath = getStaticServer() + getStaticPath(); } return staticServePath; } /** * Gets context path. * * @return context path */ public static String getContextPath() { if (null == contextPath) { contextPath = LATKE_PROPS.getProperty("contextPath"); if (null == contextPath) { contextPath = ""; } } return contextPath; } /** * Gets static path. * * @return static path */ public static String getStaticPath() { if (null == staticPath) { staticPath = LATKE_PROPS.getProperty("staticPath"); if (null == staticPath) { staticPath = getContextPath(); } } return staticPath; } /** * Gets IoC scan path. * * @return scan path */ public static String getScanPath() { if (null == scanPath) { scanPath = LATKE_PROPS.getProperty("scanPath"); } return scanPath; } /** * Sets IoC scan path with the specified scan path. * * @param scanPath the specified scan path */ public static void setScanPath(final String scanPath) { Latkes.scanPath = scanPath; } /** * Gets runtime configuration of a service specified by the given service name. * * <p> * If current runtime environment is local, returns local in any case. * </p> * * @param serviceName the given service name * @return runtime configuration, returns {@code null} if not found */ public static RuntimeEnv getRuntime(final String serviceName) { if (RuntimeEnv.LOCAL == getRuntimeEnv()) { return RuntimeEnv.LOCAL; } final String value = LATKE_PROPS.getProperty(serviceName); if (null == value) { LOGGER.log(Level.WARN, "Rutnime service[name={0}] is undefined, please configure it in latkes.properties", serviceName); return null; } return RuntimeEnv.valueOf(value); } /** * Initializes {@linkplain RuntimeEnv runtime environment}. * * <p> * Sets the current {@link RuntimeMode runtime mode} to {@link RuntimeMode#DEVELOPMENT development mode}. * </p> * * @see RuntimeEnv */ public static void initRuntimeEnv() { if (null != runtimeEnv) { return; } LOGGER.log(Level.TRACE, "Initializes runtime environment from configuration file"); final String runtimeEnvValue = LATKE_PROPS.getProperty("runtimeEnv"); if (null != runtimeEnvValue) { runtimeEnv = RuntimeEnv.valueOf(runtimeEnvValue); } runtimeEnv = RuntimeEnv.LOCAL; if (null == runtimeMode) { final String runtimeModeValue = LATKE_PROPS.getProperty("runtimeMode"); if (null != runtimeModeValue) { runtimeMode = RuntimeMode.valueOf(runtimeModeValue); } else { LOGGER.log(Level.TRACE, "Can't parse runtime mode in latke.properties, default to [PRODUCTION]"); runtimeMode = RuntimeMode.PRODUCTION; } } LOGGER.log(Level.INFO, "Latke is running on [{0}] with mode [{1}]", new Object[] { Latkes.getRuntimeEnv(), Latkes.getRuntimeMode() }); if (RuntimeEnv.LOCAL == runtimeEnv) { // Read local database configurations final RuntimeDatabase runtimeDatabase = getRuntimeDatabase(); LOGGER.log(Level.INFO, "Runtime database is [{0}]", runtimeDatabase); if (RuntimeDatabase.H2 == runtimeDatabase) { final String newTCPServer = Latkes.getLocalProperty("newTCPServer"); if ("true".equals(newTCPServer)) { LOGGER.log(Level.INFO, "Starting H2 TCP server"); final String jdbcURL = Latkes.getLocalProperty("jdbc.URL"); if (Strings.isEmptyOrNull(jdbcURL)) { throw new IllegalStateException("The jdbc.URL in local.properties is required"); } final String[] parts = jdbcURL.split(":"); if (parts.length != Integer.valueOf("5")/* CheckStyle.... */) { throw new IllegalStateException( "jdbc.URL should like [jdbc:h2:tcp://localhost:8250/~/] (the port part is required)"); } String port = parts[parts.length - 1]; port = StringUtils.substringBefore(port, "/"); LOGGER.log(Level.TRACE, "H2 TCP port [{0}]", port); try { h2 = org.h2.tools.Server .createTcpServer(new String[] { "-tcpPort", port, "-tcpAllowOthers" }).start(); } catch (final SQLException e) { final String msg = "H2 TCP server create failed"; LOGGER.log(Level.ERROR, msg, e); throw new IllegalStateException(msg); } LOGGER.info("Started H2 TCP server"); } } } locale = new Locale("en_US"); } /** * Gets the runtime environment. * * @return runtime environment */ public static RuntimeEnv getRuntimeEnv() { if (null == Latkes.runtimeEnv) { throw new RuntimeException("Runtime enviornment has not been initialized!"); } return Latkes.runtimeEnv; } /** * Sets the runtime mode with the specified mode. * * @param runtimeMode the specified mode */ public static void setRuntimeMode(final RuntimeMode runtimeMode) { Latkes.runtimeMode = runtimeMode; } /** * Gets the runtime mode. * * @return runtime mode */ public static RuntimeMode getRuntimeMode() { if (null == Latkes.runtimeMode) { throw new RuntimeException("Runtime mode has not been initialized!"); } return Latkes.runtimeMode; } /** * Gets the runtime database. * * @return runtime database */ public static RuntimeDatabase getRuntimeDatabase() { if (RuntimeEnv.LOCAL != runtimeEnv) { throw new RuntimeException( "Underlying database can be specified when Latke runs on [LOCAL] environment only, " + "current runtime enviornment [" + runtimeEnv + ']'); } final String runtimeDatabase = LOCAL_PROPS.getProperty("runtimeDatabase"); if (null == runtimeDatabase) { throw new RuntimeException("Please configures runtime database in local.properties!"); } final RuntimeDatabase ret = RuntimeDatabase.valueOf(runtimeDatabase); if (null == ret) { throw new RuntimeException("Please configures a valid runtime database in local.properties!"); } return ret; } /** * Sets the locale with the specified locale. * * @param locale the specified locale */ public static void setLocale(final Locale locale) { Latkes.locale = locale; } /** * Gets the locale. If the {@link #locale} has not been initialized, invoking this method will throw * {@link RuntimeException}. * * @return the locale */ public static Locale getLocale() { if (null == locale) { throw new RuntimeException("Default locale has not been initialized!"); } return locale; } /** * Determines whether Latkes runs with a JDBC database. * * @return {@code true} if Latkes runs with a JDBC database, returns {@code false} otherwise */ public static boolean runsWithJDBCDatabase() { return RuntimeEnv.LOCAL == Latkes.getRuntimeEnv(); } /** * Gets a property specified by the given key from file "local.properties". * * @param key the given key * @return the value, returns {@code null} if not found */ public static String getLocalProperty(final String key) { return LOCAL_PROPS.getProperty(key); } /** * Checks whether the remote interfaces are enabled. * * @return {@code true} if the remote interfaces enabled, returns {@code false} otherwise */ public static boolean isRemoteEnabled() { return !REMOTE_PROPS.isEmpty(); } /** * Gets a property specified by the given key from file "remote.properties". * * @param key the given key * @return the value, returns {@code null} if not found */ public static String getRemoteProperty(final String key) { return REMOTE_PROPS.getProperty(key); } /** * Shutdowns Latke. */ public static void shutdown() { try { if (RuntimeEnv.LOCAL != getRuntimeEnv()) { return; } Connections.shutdownConnectionPool(); final RuntimeDatabase runtimeDatabase = getRuntimeDatabase(); switch (runtimeDatabase) { case H2: final String newTCPServer = Latkes.getLocalProperty("newTCPServer"); if ("true".equals(newTCPServer)) { h2.stop(); h2.shutdown(); LOGGER.log(Level.INFO, "Closed H2 TCP server"); } break; default: } CronService.shutdown(); LocalThreadService.EXECUTOR_SERVICE.shutdown(); } catch (final Exception e) { LOGGER.log(Level.ERROR, "Shutdowns Latke failed", e); } Lifecycle.endApplication(); // This manually deregisters JDBC driver, which prevents Tomcat from complaining about memory leaks final Enumeration<Driver> drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { final Driver driver = drivers.nextElement(); try { DriverManager.deregisterDriver(driver); LOGGER.log(Level.TRACE, "Deregistered JDBC driver [" + driver + "]"); } catch (final SQLException e) { LOGGER.log(Level.ERROR, "Deregister JDBC driver [" + driver + "] failed", e); } } } /** * Sets time zone by the specified time zone id. * * @param timeZoneId the specified time zone id */ public static void setTimeZone(final String timeZoneId) { final TimeZone timeZone = TimeZone.getTimeZone(timeZoneId); Templates.MAIN_CFG.setTimeZone(timeZone); Templates.MOBILE_CFG.setTimeZone(timeZone); } /** * Loads skin with the specified directory name. * * @param skinDirName the specified directory name */ public static void loadSkin(final String skinDirName) { LOGGER.debug("Loading skin [dirName=" + skinDirName + ']'); final ServletContext servletContext = AbstractServletListener.getServletContext(); Templates.MAIN_CFG.setServletContextForTemplateLoading(servletContext, "skins/" + skinDirName); Latkes.setTimeZone("Asia/Shanghai"); LOGGER.info("Loaded skins...."); } /** * Gets the skin name for the specified skin directory name. The skin name was configured in skin.properties * file({@code name} as the key) under skin directory specified by the given skin directory name. * * @param skinDirName the given skin directory name * @return skin name, returns {@code null} if not found or error occurs */ public static String getSkinName(final String skinDirName) { try { final Properties ret = new Properties(); final File file = getWebFile("/skins/" + skinDirName + "/skin.properties"); ret.load(new FileInputStream(file)); return ret.getProperty("name"); } catch (final Exception e) { LOGGER.log(Level.ERROR, "Read skin configuration error[msg={0}]", e.getMessage()); return null; } } /** * Gets a file in web application with the specified path. * * @param path the specified path * @return file, * @see ServletContext#getResource(String) * @see ServletContext#getResourceAsStream(String) */ public static File getWebFile(final String path) { final ServletContext servletContext = AbstractServletListener.getServletContext(); File ret; try { final URL resource = servletContext.getResource(path); if (null == resource) { return null; } ret = FileUtils.toFile(resource); if (null == ret) { final File tempdir = (File) servletContext.getAttribute("javax.servlet.context.tempdir"); ret = new File(tempdir.getPath() + path); FileUtils.copyURLToFile(resource, ret); ret.deleteOnExit(); } return ret; } catch (final Exception e) { LOGGER.log(Level.ERROR, "Reads file [path=" + path + "] failed", e); return null; } } /** * Sets server scheme with the specified server scheme. * * @param serverScheme the specified server scheme */ public static void setServerScheme(final String serverScheme) { Latkes.serverScheme = serverScheme; } /** * Sets static server scheme with the specified static server scheme. * * @param staticServerScheme the specified static server scheme */ public static void setStaticServerScheme(final String staticServerScheme) { Latkes.staticServerScheme = staticServerScheme; } /** * Sets server host with the specified server host. * * @param serverHost the specified server host */ public static void setServerHost(final String serverHost) { Latkes.serverHost = serverHost; } /** * Sets static server host with the specified static server host. * * @param staticServerHost the specified static server host */ public static void setStaticServerHost(final String staticServerHost) { Latkes.staticServerHost = staticServerHost; } /** * Sets server port with the specified server port. * * @param serverPort the specified server port */ public static void setServerPort(final String serverPort) { Latkes.serverPort = serverPort; } /** * Sets static server port with the specified static server port. * * @param staticServerPort the specified static server port */ public static void setStaticServerPort(final String staticServerPort) { Latkes.staticServerPort = staticServerPort; } /** * Sets context path with the specified context path. * * @param contextPath the specified context path */ public static void setContextPath(final String contextPath) { Latkes.contextPath = contextPath; } /** * Sets static path with the specified static path. * * @param staticPath the specified static path */ public static void setStaticPath(final String staticPath) { Latkes.staticPath = staticPath; } /** * Sets static resource version with the specified static resource version. * * @param staticResourceVersion the specified static resource version */ public static void setStaticResourceVersion(final String staticResourceVersion) { Latkes.staticResourceVersion = staticResourceVersion; } /** * Private constructor. */ private Latkes() { } }