/* * Copyright 2013 The Skfiy Open Association. * * 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.skfiy.typhon.startup; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; import java.util.Properties; import org.apache.commons.digester.Digester; import org.skfiy.typhon.Globals; import org.skfiy.typhon.LifecycleException; import org.skfiy.typhon.Server; import org.skfiy.typhon.TyphonException; import org.skfiy.util.ResourceUtils; import org.skfiy.util.SystemPropertyUtils; import org.slf4j.LoggerFactory; /** * 通过Typhon启动应用程序和停止应用程序. * * @author Kevin Zou <kevinz@skfiy.org> */ public final class Typhon { private boolean await; private Server server; /** * 是否启动Socket停止服务功能. * * @param await The value is true/false */ public void setAwait(final boolean await) { this.await = await; } /** * 设置应用主服务对象. * * @param server Server object */ public void setServer(final Server server) { this.server = server; } /** * 加载server.xml, typhon.properties配置文件. */ public void load() { try { // Create and execute our Digester Digester digester = createStartDigester(); digester.push(this); digester.parse(ResourceUtils.getURL("classpath:server.xml").openStream()); } catch (Exception e) { throw new TyphonException("加载server.xml失败", e); } // load typhon.properties loadProperties(); } /** * 启动应用. */ public void start() { if (server == null) { load(); } try { server.init(); server.start(); } catch (LifecycleException ex) { throw new TyphonException(ex); } // await if (await) { Thread shutdown = new Thread(new Runnable() { @Override public void run() { await(); } }, "Typhon-Shutdown"); shutdown.setDaemon(true); shutdown.start(); } } /** * 停止应用. */ public void stop() { if (server == null) { try { // Create and execute our Digester Digester digester = createStopDigester(); digester.push(this); digester.parse(ResourceUtils.getFile("classpath:server.xml")); // load typhon.properties loadProperties(); } catch (Exception e) { throw new TyphonException(e); } Socket socket = null; try { socket = new Socket(server.getHost(), server.getPort()); OutputStream out = socket.getOutputStream(); out.write(server.getShutdown().getBytes(StandardCharsets.UTF_8)); out.flush(); } catch (Exception e) { throw new TyphonException(e); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // nothing } } } return; } try { server.stop(); server.destroy(); } catch (LifecycleException ex) { throw new TyphonException(ex); } } /** * Create and configure the Digester we will be using for startup. */ private Digester createStartDigester() { long t1=System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); digester.setValidating(false); digester.setClassLoader(Server.class.getClassLoader()); digester.addObjectCreate( "Server", "org.skfiy.typhon.kernel.StandardServer"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer", "org.skfiy.typhon.Server"); digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element "className"); digester.addSetProperties("Server/Listener"); digester.addSetNext("Server/Listener", "addListener", "org.skfiy.typhon.LifecycleListener"); digester.addObjectCreate( "Server/Service", "org.skfiy.typhon.kernel.StandardService"); digester.addSetProperties("Server/Service"); digester.addSetNext( "Server/Service", "addService", "org.skfiy.typhon.Service"); digester.addObjectCreate("Server/Service/Listener", null, // MUST be specified in the element "className"); digester.addSetProperties("Server/Service/Listener"); digester.addSetNext("Server/Service/Listener", "addListener", "org.skfiy.typhon.LifecycleListener"); // connector digester.addObjectCreate("Server/Service/Connector", "org.skfiy.typhon.net.NettyConnector", "className"); digester.addSetProperties("Server/Service/Connector"); digester.addSetNext("Server/Service/Connector", "addConnector", "org.skfiy.typhon.Connector"); digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST be specified in the element "className"); digester.addSetProperties("Server/Service/Connector/Listener"); digester.addSetNext("Server/Service/Connector/Listener", "addListener", "org.skfiy.typhon.LifecycleListener"); // container // digester.addObjectCreate("Server/Service/Container", null, "className"); // digester.addSetProperties("Server/Service/Container"); // digester.addSetNext("Server/Service/Container", "setContainer", // "org.skfiy.typhon.Container"); long t2 = System.currentTimeMillis(); LoggerFactory.getLogger(Globals.CONSOLE_LOG_NAME) .debug("Digester for server.xml created " + (t2 - t1)); return digester; } /** * Create and configure the Digester we will be using for shutdown. */ private Digester createStopDigester() { // Initialize the digester Digester digester = new Digester(); // Configure the rules we need for shutting down digester.addObjectCreate( "Server", "org.skfiy.typhon.kernel.StandardServer"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer", "org.skfiy.typhon.Server"); return digester; } private void loadProperties() { InputStream stream = null; try { Properties props = new Properties(); stream = ResourceUtils.getURL("classpath:typhon.properties").openStream(); props.load(stream); // loading... ext.properties loadExtProperties(props); // put all to system properties System.getProperties().putAll(props); for (Map.Entry<Object, Object> entry : props.entrySet()) { String val = SystemPropertyUtils.resolvePlaceholders( (String) entry.getValue()); System.setProperty((String) entry.getKey(), val); } } catch (FileNotFoundException ex) { throw new TyphonException("没有在classpath环境中找到typhon.properties文件", ex); } catch (IOException ex) { throw new TyphonException("加载typhon.propertiesr失败", ex); } finally { try { stream.close(); } catch (Exception ex) { // nothing } } } private void loadExtProperties(Properties props) { File extFile = new File(System.getProperty("typhon.home"), "/conf/ext.properties"); if (extFile.exists()) { InputStream stream = null; try { stream = new FileInputStream(extFile); props.load(stream); } catch (IOException ex) { throw new TyphonException("加载 $TYPHON_HOME/conf/ext.properties 失败", ex); } finally { try { stream.close(); } catch (Exception ex) { } } } } private void await() { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(); serverSocket.bind( new InetSocketAddress( server.getHost(), server.getPort()), 1); } catch (IOException e) { throw new TyphonException(e); } for (;;) { Socket socket = null; try { socket = serverSocket.accept(); InputStream in = socket.getInputStream(); byte[] cur = server.getShutdown().getBytes(StandardCharsets.UTF_8); byte[] buf = new byte[cur.length]; int l = in.read(buf); // 接收到退出命令 if (l == cur.length && Arrays.equals(cur, buf)) { break; } } catch (IOException e) { } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // nothing } } } } try { serverSocket.close(); } catch (IOException e) { // nothing } // 停止服务 stop(); } }