/*
* Copyright amoeba.meidusa.com
*
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU AFFERO GENERAL PUBLIC LICENSE for more details.
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.meidusa.amoeba.server;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.meidusa.amoeba.config.BeanObjectEntityConfig;
import com.meidusa.amoeba.config.ConfigUtil;
import com.meidusa.amoeba.config.loader.AmoebaContextLoader;
import com.meidusa.amoeba.context.ProxyRuntimeContext;
import com.meidusa.amoeba.monitor.MonitorConstant;
import com.meidusa.amoeba.monitor.ShutdownClient;
import com.meidusa.amoeba.monitor.packet.MonitorCommandPacket;
import com.meidusa.amoeba.net.ConnectionManager;
import com.meidusa.amoeba.runtime.PriorityShutdownHook;
import com.meidusa.amoeba.service.Service;
import com.meidusa.amoeba.util.Reporter;
import com.meidusa.amoeba.util.StringUtil;
/**
*
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
*
*/
public class AmoebaProxyServer {
private static Logger log = Logger.getLogger(AmoebaProxyServer.class);
private static Logger repoterLog = Logger.getLogger("report");
/** Used to generate "state of server" reports. */
protected static ArrayList<Reporter> reporters = new ArrayList<Reporter>();
/** The time at which the server was started. */
protected static long serverStartTime = System.currentTimeMillis();
/** The last time at which {@link #generateReport} was run. */
protected static long lastReportStamp = serverStartTime;
public static void registerReporter(Reporter reporter) {
reporters.add(reporter);
}
/**
* Generates a report for all system services registered as a
* {@link Reporter}.
*/
public static String generateReport() {
return generateReport(System.currentTimeMillis(), false);
}
/**
* Generates and logs a "state of server" report.
*/
protected static String generateReport(long now, boolean reset) {
long sinceLast = now - lastReportStamp;
long uptime = now - serverStartTime;
StringBuilder report = new StringBuilder(" State of server report:"+StringUtil.LINE_SEPARATOR);
report.append("- Uptime: ").append(StringUtil.intervalToString(uptime)).append(StringUtil.LINE_SEPARATOR);
report.append("- Report period: ").append(StringUtil.intervalToString(sinceLast)).append(StringUtil.LINE_SEPARATOR);
// report on the state of memory
Runtime rt = Runtime.getRuntime();
long total = rt.totalMemory(), max = rt.maxMemory();
long used = (total - rt.freeMemory());
report.append("- Memory: ").append(used / 1024).append("k used, ");
report.append(total / 1024).append("k total, ");
report.append(max / 1024).append("k max").append(StringUtil.LINE_SEPARATOR);
for (int ii = 0; ii < reporters.size(); ii++) {
Reporter rptr = reporters.get(ii);
try {
rptr.appendReport(report, now, sinceLast, reset,repoterLog.getLevel());
} catch (Throwable t) {
log.error("Reporter choked [rptr=" + rptr + "].", t);
}
}
// only reset the last report time if this is a periodic report
if (reset) {
lastReportStamp = now;
}
return report.toString();
}
protected static void logReport(String report) {
repoterLog.info(report);
}
/**
* @param args
* @throws IOException
* @throws Exception
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void run(String[] args, BundleContext bundleContext) throws Exception {
final Logger logger = Logger.getLogger(AmoebaProxyServer.class);
String backendBundleName = bundleContext.getProperty("amoeba.context.backend");
Bundle backendBundle = null;
if (backendBundleName != null) {
for(Bundle bundle : bundleContext.getBundles()) {
if (bundle.getSymbolicName().equalsIgnoreCase(backendBundleName)) {
backendBundle = bundle;
break;
}
}
}
if (backendBundle == null) {
logger.error("could not find backend bundle " + backendBundleName);
System.exit(-1);
}
// Proxy Runtime Context最先建立
String contextClass = bundleContext.getProperty("amoeba.context.class");
if(contextClass != null){
ProxyRuntimeContext proxyRuntimeContext = (ProxyRuntimeContext)backendBundle.loadClass(contextClass).newInstance();
ProxyRuntimeContext.setInstance(proxyRuntimeContext);
}
// 默认是ProxyRuntimeContext
else {
ProxyRuntimeContext proxyRuntimeContext = (ProxyRuntimeContext)backendBundle.loadClass(ProxyRuntimeContext.class.getName()).newInstance();
ProxyRuntimeContext.setInstance(proxyRuntimeContext);
}
ProxyRuntimeContext.getInstance().setBackendBundle(backendBundle);
String level = bundleContext.getProperty("benchmark.level");
if (!StringUtil.isEmpty(level)) {
System.setProperty("benchmark.level", level);
}
// 设置Amoeba home路径,考虑相对路径
String amoebaHomeProp = bundleContext.getProperty("amoeba.home");
String relatedPath = new File(amoebaHomeProp).getCanonicalPath();
File amoebaHome = new File(relatedPath);
if (amoebaHome == null || !amoebaHome.exists()) {
logger.error("could not setup amoeba home path :"+amoebaHomeProp);
System.exit(-1);
}
String amoebaHomePath = amoebaHome.getAbsolutePath();
ProxyRuntimeContext.getInstance().setAmoebaHomePath(amoebaHomePath);
if(args.length>=1){
ShutdownClient client = new ShutdownClient(MonitorConstant.APPLICATION_NAME);
MonitorCommandPacket packet = new MonitorCommandPacket();
if("start".equalsIgnoreCase(args[0])){
packet.funType = MonitorCommandPacket.FUN_TYPE_PING;
if(client.run(packet)){
System.out.println("amoeba server is running with port="+client.getPort());
System.exit(-1);
}
}else{
packet.funType = MonitorCommandPacket.FUN_TYPE_AMOEBA_SHUTDOWN;
if(client.run(packet)){
System.out.println("amoeba server shutting down with port="+client.getPort());
}else{
System.out.println("amoeba server not running with port="+client.getPort());
}
System.exit(0);
}
}else{
System.out.println("amoeba start|stop");
System.exit(0);
}
ServiceReference<AmoebaContextLoader> ref = backendBundle.getBundleContext().getServiceReference(AmoebaContextLoader.class);
AmoebaContextLoader loader = backendBundle.getBundleContext().getService(ref);
loader.setAmoebaContext(ProxyRuntimeContext.getInstance());
ProxyRuntimeContext.getInstance().setAmoebaLoader(loader);
ProxyRuntimeContext.getInstance().initConfig();
ProxyRuntimeContext.getInstance().init();
registerReporter(ProxyRuntimeContext.getInstance());
for(ConnectionManager connMgr :ProxyRuntimeContext.getInstance().getConnectionManagerList().values()){
registerReporter(connMgr);
}
Map<String,Object> context = new HashMap<String,Object>();
context.putAll(ProxyRuntimeContext.getInstance().getConnectionManagerList());
List<BeanObjectEntityConfig> serviceConfigList = ProxyRuntimeContext.getInstance().getConfig().getServiceConfigList();
for(BeanObjectEntityConfig serverConfig : serviceConfigList){
Service service = (Service)serverConfig.createBeanObject(false, context);
service.init();
service.start();
PriorityShutdownHook.addShutdowner(service);
registerReporter(service);
}
new Thread(){
{
this.setDaemon(true);
this.setName("Amoeba Report Thread");
}
public void run(){
while(true){
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
}
try{
logReport(generateReport());
}catch(Exception e){
logger.error("report error",e);
}
}
}
}.start();
}
}