/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.openejb.server;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.monitoring.LocalMBeanServer;
import org.apache.openejb.monitoring.ManagedMBean;
import org.apache.openejb.monitoring.ObjectNameBuilder;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.xbean.finder.ResourceFinder;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @version $Rev$ $Date$
* @org.apache.xbean.XBean element="simpleServiceManager"
*/
public class SimpleServiceManager extends ServiceManager {
private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_SERVER, SimpleServiceManager.class);
private static ObjectName objectName = null;
private ServerService[] daemons;
private volatile boolean stopped = false;
private final ServiceFinder serviceFinder;
public SimpleServiceManager() {
this(new SimpleServiceFinder("META-INF/"));
}
public SimpleServiceManager(final ServiceFinder serviceFinder) {
this.serviceFinder = serviceFinder;
}
// Have properties files (like xinet.d) that specifies what daemons to
// Look into the xinet.d file structure again
// conf/server.d/
// admin.properties
// ejbd.properties
// webadmin.properties
// telnet.properties
// corba.properties
// soap.properties
// xmlrpc.properties
// httpejb.properties
// webejb.properties
// xmlejb.properties
// Each contains the class name of the daemon implamentation
// The port to use
// whether it's turned on
// May be reusable elsewhere, move if another use occurs
public static class SimpleServiceFinder implements ServiceFinder {
private final ResourceFinder resourceFinder;
private final ClassLoader classLoader;
public SimpleServiceFinder(final String basePath) {
this(basePath, Thread.currentThread().getContextClassLoader());
}
public SimpleServiceFinder(final String basePath, final ClassLoader classLoader) {
this.resourceFinder = new ResourceFinder(basePath, classLoader);
this.classLoader = classLoader;
}
@Override
public Map<String, Properties> mapAvailableServices(final Class interfase) throws IOException, ClassNotFoundException {
final Map<String, Properties> service = resourceFinder.mapAvailableProperties(ServerService.class.getName());
for (final Map.Entry<String, Properties> entry : service.entrySet()) {
final String name = entry.getKey();
final Properties properties = entry.getValue();
String className = properties.getProperty("className");
if (className == null) {
className = properties.getProperty("classname");
if (className == null) {
className = properties.getProperty("server");
}
}
final Class impl = classLoader.loadClass(className);
properties.put(interfase, impl);
final String rawProperties = resourceFinder.findString(interfase.getName() + "/" + name);
properties.put(Properties.class, rawProperties);
}
return service;
}
}
private static ObjectName getDiscoveryRegistryObjectName() {
if (null == objectName) {
final ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb");
jmxName.set("type", "Server");
jmxName.set("name", "DiscoveryRegistry");
objectName = jmxName.build();
}
return objectName;
}
@Override
public void init() throws Exception {
try {
ServiceLogger.MDCput("SERVER", "main");
final InetAddress localhost = InetAddress.getLocalHost();
ServiceLogger.MDCput("HOST", localhost.getHostName());
} catch (Throwable e) {
//Ignore
}
final DiscoveryRegistry registry = new DiscoveryRegistry();
// register the mbean
try {
LocalMBeanServer.get().registerMBean(new ManagedMBean(registry), getDiscoveryRegistryObjectName());
} catch (Throwable e) {
logger.error("Failed to register 'openejb' MBean", e);
}
SystemInstance.get().setComponent(DiscoveryRegistry.class, registry);
final Map<String, Properties> availableServices = this.serviceFinder.mapAvailableServices(ServerService.class);
final List<ServerService> enabledServers = initServers(availableServices);
daemons = enabledServers.toArray(new ServerService[enabledServers.size()]);
stopped = false;
}
@Override
public synchronized void start(final boolean block) throws ServiceException {
if (stopped) {
throw new ServiceException("Stop has already been called on ServiceManager");
}
final boolean display = SystemInstance.get().getOptions().get("openejb.nobanner", (String) null) == null;
// starting then displaying to get a more relevant log
final Exception[] errors = new Exception[daemons.length];
for (int i = 0; i < daemons.length; i++) {
final ServerService d = daemons[i];
try {
d.start();
errors[i] = null;
} catch (Exception e) {
errors[i] = e;
LOGGER.info("Can't start service " + d.getName(), e);
}
}
if (display) {
LOGGER.info(" ** Bound Services **");
printRow("NAME", "IP", "PORT");
}
for (int i = 0; i < daemons.length; i++) {
final ServerService d = daemons[i];
if (errors[i] == null) {
if (display && d.getPort() != -1) {
printRow(d.getName(), d.getIP(), d.getPort() + "");
}
} else {
logger.fatal("Service Start Failed: " + d.getName() + " " + d.getIP() + " " + d.getPort() + ": " + errors[i].getMessage());
if (display) {
printRow(d.getName(), "----", "FAILED");
}
}
}
if (display) {
LOGGER.info("-------");
LOGGER.info("Ready!");
}
if (!block) {
return;
}
/*
* This will cause the user thread (the thread that keeps the
* vm alive) to go into a state of constant waiting.
* Each time the thread is woken up, it checks to see if
* it should continue waiting.
*
* To stop the thread (and the VM), just call the stop method
* which will set 'stop' to true and notify the user thread.
*/
try {
while (!stopped) {
this.wait(Long.MAX_VALUE);
}
} catch (Throwable t) {
logger.fatal("Unable to keep the server thread alive. Received exception: " + t.getClass().getName() + " : " + t.getMessage());
}
logger.info("Stopping Remote Server");
}
@Override
public synchronized void stop() throws ServiceException {
logger.info("Stopping server services");
stopped = true;
final ServerService[] services = daemons.clone();
final MBeanServer server = LocalMBeanServer.get();
for (final ServerService service : services) {
if (LocalMBeanServer.isJMXActive()) {
final ObjectName on = getObjectName(service.getName());
if (server.isRegistered(on)) {
try {
server.unregisterMBean(on);
} catch (Exception ignored) {
// no-op
}
}
}
try {
service.stop();
} catch (ServiceException e) {
logger.fatal("Service Shutdown Failed: " + service.getName() + ". Exception: " + e.getMessage(), e);
}
}
// De-register the mbean
try {
final ObjectName objectName = getDiscoveryRegistryObjectName();
if (server.isRegistered(objectName)) {
server.unregisterMBean(objectName);
}
} catch (Throwable e) {
logger.warning("Failed to de-register the 'openejb' mbean", e);
}
setServiceManager(null);
notifyAll();
}
private void printRow(String col1, String col2, String col3) {
col1 += " ";
col1 = col1.substring(0, 20);
col2 += " ";
col2 = col2.substring(0, 15);
col3 += " ";
col3 = col3.substring(0, 6);
final String sb = " " + col1 + " " + col2 + " " + col3;
LOGGER.info(sb);
}
public ServerService[] getDaemons() {
return daemons;
}
}