//$Header: /cvsroot-fuse/mec-as2/39/mendelson/comm/as2/server/AS2Server.java,v 1.1 2012/04/18 14:10:38 heller Exp $
package de.mendelson.comm.as2.server;
import de.mendelson.comm.as2.timing.MDNReceiptController;
import de.mendelson.Copyright;
import de.mendelson.comm.as2.AS2ServerVersion;
import de.mendelson.comm.as2.AS2ShutdownThread;
import de.mendelson.comm.as2.cert.CertificateCEMController;
import de.mendelson.util.security.cert.CertificateManager;
import de.mendelson.comm.as2.database.DBDriverManager;
import de.mendelson.comm.as2.database.DBServer;
import de.mendelson.comm.as2.log.DBLoggingHandler;
import de.mendelson.comm.as2.preferences.PreferencesAS2;
import de.mendelson.comm.as2.send.DirPollManager;
import de.mendelson.comm.as2.sendorder.SendOrderAccessDB;
import de.mendelson.comm.as2.sendorder.SendOrderReceiver;
import de.mendelson.comm.as2.timing.CertificateExpireController;
import de.mendelson.comm.as2.timing.MessageDeleteController;
import de.mendelson.comm.as2.timing.StatisticDeleteController;
import de.mendelson.util.MecResourceBundle;
import de.mendelson.util.clientserver.ClientServer;
import de.mendelson.util.clientserver.log.ClientServerLoggingHandler;
import de.mendelson.util.clientserver.user.DefaultPermissionDescription;
import de.mendelson.util.log.DailySubdirFileLoggingHandler;
import de.mendelson.util.rmi.MecRemote;
import de.mendelson.util.security.BCCryptoHelper;
import de.mendelson.util.security.cert.KeystoreStorage;
import de.mendelson.util.security.cert.KeystoreStorageImplFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.net.URL;
import java.rmi.ConnectException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.Connection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.mortbay.jetty.Server;
import org.mortbay.xml.XmlConfiguration;
/**
* Class to start the AS2 server
* @author S.Heller
* @version $Revision: 1.1 $
* @since build 68
*/
public class AS2Server extends AbstractAS2Server implements AS2ServerMBean {
public static final String SERVER_LOGGER_NAME = "de.mendelson.as2.server";
private static int transactionCounter = 0;
private static long rawDataSent = 0;
private static long rawDataReceived = 0;
/**Server start time in ms*/
long startTime = System.currentTimeMillis();
private Logger logger = Logger.getLogger(SERVER_LOGGER_NAME);
/**Product preferences*/
private PreferencesAS2 preferences = new PreferencesAS2();
/**Registry to register RMI services*/
private Registry registry = null;
/**DB server that is used*/
private DBServer dbServer = null;
/**Localize the output
*/
private MecResourceBundle rb = null;
private DirPollManager pollManager = null;
private CertificateManager certificateManager = null;
//DB connection
private Connection configConnection = null;
private Connection runtimeConnection = null;
/**Stores the RMI service locale to use the direct instanciation of a service instead
* of RMI if RMI client and server are running in the same VM*/
private static Hashtable<String, MecRemote> localRMIServiceMap = new Hashtable<String, MecRemote>();
private ClientServer clientserver;
private ClientServerSessionHandlerLocalhost clientServerSessionHandler = null;
private boolean startHTTPServer = false;
/**Sets if all clients may connect to this server or only clients from the servers host*/
private boolean allowAllClients = false;
/**Creates a new AS2 server and starts it
*/
public AS2Server(boolean startHTTPServer, boolean allowAllClients) throws Exception {
//Load default resourcebundle
try {
this.rb = (MecResourceBundle) ResourceBundle.getBundle(
ResourceBundleAS2Server.class.getName());
} //load up resourcebundle
catch (MissingResourceException e) {
throw new Exception("Oops..resource bundle " + e.getClassName() + " not found.");
}
this.startHTTPServer = startHTTPServer;
this.allowAllClients = allowAllClients;
AS2ServerResourceCheck resourceCheck = new AS2ServerResourceCheck();
resourceCheck.performPortCheck();
BCCryptoHelper helper = new BCCryptoHelper();
boolean unlimitedStrengthInstalled = helper.performUnlimitedStrengthJurisdictionPolicyTest();
if (!unlimitedStrengthInstalled) {
this.logger.severe(this.rb.getResourceString("fatal.limited.strength"));
System.exit(1);
}
ServerStartupSequence startup = new ServerStartupSequence(this.logger);
startup.performWork();
this.checkLock();
int clientServerCommPort = this.preferences.getInt(PreferencesAS2.CLIENTSERVER_COMM_PORT);
this.clientserver = new ClientServer(logger, clientServerCommPort);
this.clientserver.setProductName(AS2ServerVersion.getFullProductName());
this.setupClientServerSessionHandler();
this.start();
//start the partner poll threads
this.pollManager.partnerConfigurationChanged();
}
@Override
public void start() throws Exception {
this.logger.info(rb.getResourceString("server.willstart", AS2ServerVersion.getFullProductName()));
this.logger.info(Copyright.getCopyrightMessage());
this.startDBServer();
this.configConnection = DBDriverManager.getConnectionWithoutErrorHandling(DBDriverManager.DB_CONFIG, "localhost");
this.runtimeConnection = DBDriverManager.getConnectionWithoutErrorHandling(DBDriverManager.DB_RUNTIME, "localhost");
this.startHTTPServer();
this.certificateManager = new CertificateManager(this.logger);
KeystoreStorage storage = new KeystoreStorageImplFile("certificates.p12",
this.preferences.get(PreferencesAS2.KEYSTORE_PASS).toCharArray(),
BCCryptoHelper.KEYSTORE_PKCS12);
this.certificateManager.loadKeystoreCertificates(storage);
this.startSendOrderReceiver();
this.setupLogger();
this.registerRMI();
//dont cache the DNS lookup for whole lifetime of the VM
System.setProperty("networkaddress.cache.ttl", "86400");
//start control threads
MDNReceiptController receiptController = new MDNReceiptController(this.clientserver, this.configConnection, this.runtimeConnection);
receiptController.startMDNCheck();
MessageDeleteController logDeleteController = new MessageDeleteController(this.clientserver, this.configConnection, this.runtimeConnection);
logDeleteController.startAutoDeleteControl();
StatisticDeleteController statsDeleteController = new StatisticDeleteController(this.configConnection, this.runtimeConnection);
statsDeleteController.startAutoDeleteControl();
this.pollManager = new DirPollManager(this.certificateManager, this.configConnection,
this.runtimeConnection, this.clientserver);
this.clientServerSessionHandler.addServerProcessing(
new AS2ServerProcessing(this.clientserver, this.pollManager,
this.certificateManager, this.configConnection, this.runtimeConnection));
CertificateExpireController expireController = new CertificateExpireController(this.certificateManager, this.configConnection, this.runtimeConnection);
expireController.startCertExpireControl();
CertificateCEMController cemController = new CertificateCEMController(this.clientserver, this.configConnection, this.runtimeConnection, this.certificateManager);
Executors.newSingleThreadExecutor().submit(cemController);
Runtime.getRuntime().addShutdownHook(new AS2ShutdownThread(this.dbServer));
//listen for inbound client connects
this.clientserver.start();
this.logger.info(rb.getResourceString("server.started",
String.valueOf(System.currentTimeMillis() - this.startTime)));
//display the startup message on the console
System.out.println(rb.getResourceString("server.started",
String.valueOf(System.currentTimeMillis() - this.startTime)));
}
private void setupLogger() {
this.logger.setUseParentHandlers(false);
//send the log info to the attached clients of the client-server framework
this.logger.addHandler(new ClientServerLoggingHandler(this.clientServerSessionHandler));
this.logger.addHandler(new DBLoggingHandler());
//add file logger that logs in a daily subdir
this.logger.addHandler(new DailySubdirFileLoggingHandler(new File(this.preferences.get(PreferencesAS2.DIR_LOG)), "as2.log"));
this.logger.setLevel(Level.ALL);
}
private void setupClientServerSessionHandler() {
//set up session handler for incoming client requests
this.clientServerSessionHandler = new ClientServerSessionHandlerLocalhost(this.logger,
new String[]{AS2ServerVersion.getFullProductName()}, this.allowAllClients);
this.clientserver.setSessionHandler(this.clientServerSessionHandler);
this.clientServerSessionHandler.setProductName(AS2ServerVersion.getProductName());
this.clientServerSessionHandler.setPermissionDescription(new DefaultPermissionDescription());
}
@Override
public Logger getLogger() {
return (this.logger);
}
/**register a local service to the mbi server. This will skip the remote RMI
* if the request is a local request. That means: no serialization, more performance
*
* @param service Service name ot register
* @param remote Remote implementation
*/
public static void registerLocalRMI(String service, MecRemote remote) {
AS2Server.localRMIServiceMap.put(service, remote);
}
/**Returns an instance of the requested service if this has been registered to the AS2 server
* If it has not been registered, null is returned and the RMI way will be used for the client-server
* connection
*/
public static MecRemote lookupLocalRMI(String service) {
return (AS2Server.localRMIServiceMap.get(service));
}
/**Checks for a lock to prevent starting the server several times on the same machine
*
*/
private void checkLock() {
//check if lock file exists, if it exists cancel!
File lockFile = new File(AS2ServerVersion.getProductName().replace(' ', '_') + ".lock");
if (lockFile.exists()) {
DateFormat format = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
this.logger.severe(rb.getResourceString("server.already.running",
new Object[]{
lockFile.getAbsolutePath(),
format.format(new java.util.Date(lockFile.lastModified()))
}));
throw new RuntimeException(rb.getResourceString("server.already.running",
new Object[]{
lockFile.getAbsolutePath(),
format.format(new java.util.Date(lockFile.lastModified()))
}));
} else {
try {
FileWriter writer = new FileWriter(lockFile);
writer.write("");
writer.flush();
writer.close();
} catch (Exception e) {
this.logger.severe("checkLock: " + e.getMessage());
System.exit(1);
}
}
}
private void startSendOrderReceiver() throws Exception {
//reset the send order state of available send orders back to waiting
SendOrderAccessDB sendOrderAccess = new SendOrderAccessDB(this.configConnection, this.runtimeConnection);
sendOrderAccess.resetAllToWaiting();
SendOrderReceiver receiver = new SendOrderReceiver(this.configConnection, this.runtimeConnection,
this.clientserver);
Executors.newSingleThreadExecutor().submit(receiver);
}
private Server startHTTPServer() throws Exception {
//start the HTTP server if this is requested
if (this.startHTTPServer) {
System.setProperty("jetty.home", new File("jetty").getAbsolutePath());
URL serviceConfig = new File("./jetty/etc/jetty.xml").toURI().toURL();
XmlConfiguration xmlConfiguration = new XmlConfiguration(serviceConfig);
org.mortbay.jetty.Server server = new org.mortbay.jetty.Server();
xmlConfiguration.configure(server);
server.start();
return (server);
} else {
this.logger.info(rb.getResourceString("server.nohttp"));
}
return (null);
}
/**Starts the database server*/
private void startDBServer() throws Exception {
//start the database server
this.dbServer = new DBServer();
this.dbServer.startup();
while (true) {
try {
Connection testConnection = DBDriverManager.getConnectionWithoutErrorHandling(DBDriverManager.DB_CONFIG, "localhost");
testConnection.close();
break;
} catch (Throwable e) {
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
//nop
}
}
}
return;
}
/**Registers the RMI services an lists them
*/
private void registerRMI() throws Exception {
AS2ServerRemoteImpl remote = new AS2ServerRemoteImpl(this.clientserver, this.certificateManager,
this.configConnection, this.runtimeConnection);
//register local services
AS2Server.registerLocalRMI(this.preferences.get(PreferencesAS2.SERVER_RMI_SERVICE), remote);
int rmiPort = this.preferences.getInt(PreferencesAS2.SERVER_RMI_PORT);
try {
//check for existing registry and add service to this port, will throw an exception
//if no registry has been found
this.registry = LocateRegistry.getRegistry(rmiPort);
//this rebind attempt should throw an exception if there is no registry so far. If there is a registry
//of an other process we will use it. This will result in some trouble if the other process is shut down...
registry.rebind(this.preferences.get(PreferencesAS2.SERVER_RMI_SERVICE),
remote);
this.logger.info("Existing RMI registry found at port " + rmiPort + ", binding services there.");
String[] services = this.registry.list();
this.logger.info("Services bound, the following services are available now:");
for (int i = 0; i < services.length; i++) {
this.logger.info(services[i]);
}
} catch (ConnectException e) {
//ok, there is no registry running on this machine
this.registry = LocateRegistry.createRegistry(rmiPort);
registry.rebind(this.preferences.get(PreferencesAS2.SERVER_RMI_SERVICE),
remote);
} catch (RemoteException e) {
this.logger.info("A running registry was found at port " + rmiPort + ". To allow " + AS2ServerVersion.getProductNameShortcut() + " to use this registry you have to add the " + AS2ServerVersion.getProductNameShortcut() + " jar to the CLASSPATH" + " environment variable of the registry hosting program.");
this.logger.severe("Detailed exception information:");
this.logger.severe(e.getMessage());
System.exit(-1);
}
}
@Override
public int getPort() {
return (this.preferences.getInt(PreferencesAS2.CLIENTSERVER_COMM_PORT));
}
/**MBean interface*/
@Override
public long getUsedMemoryInBytes() {
return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
}
/**MBean interface*/
@Override
public long getTotalMemoryInBytes() {
return (Runtime.getRuntime().totalMemory());
}
/**MBean interface*/
@Override
public String getServerVersion() {
return (AS2ServerVersion.getProductName() + " " + AS2ServerVersion.getVersion() + " " + AS2ServerVersion.getBuild());
}
/**MBean interface*/
@Override
public long getUptimeInMS() {
return (System.currentTimeMillis() - this.startTime);
}
@Override
public long getRawDataSentInBytesInUptime() {
return (rawDataSent);
}
@Override
public long getRawDataReceivedInBytesInUptime() {
return (rawDataReceived);
}
@Override
public long getTransactionCountInUptime() {
return (transactionCounter);
}
public static synchronized void incTransactionCounter() {
transactionCounter++;
}
public static synchronized void incRawSentData(long size) {
rawDataSent += size;
}
public static synchronized void incRawReceivedData(long size) {
rawDataReceived += size;
}
/**Deletes the lock file*/
public static void deleteLockFile() {
File lockFile = new File(AS2ServerVersion.getProductName().replace(' ', '_') + ".lock");
lockFile.delete();
}
}