package org.jboss.as.messaging;
import static org.jboss.as.messaging.MessagingLogger.ROOT_LOGGER;
import static org.jboss.as.messaging.MessagingMessages.MESSAGES;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.MBeanServer;
import org.hornetq.api.core.DiscoveryGroupConfiguration;
import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.core.config.BroadcastGroupConfiguration;
import org.hornetq.core.config.Configuration;
import org.hornetq.core.config.impl.ConfigurationImpl;
import org.hornetq.core.journal.impl.AIOSequentialFileFactory;
import org.hornetq.core.server.HornetQServer;
import org.hornetq.core.server.JournalType;
import org.hornetq.core.server.impl.HornetQServerImpl;
import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.network.OutboundSocketBinding;
import org.jboss.as.network.SocketBinding;
import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.inject.MapInjector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
/**
* Service configuring and starting the {@code HornetQService}.
*
* @author scott.stark@jboss.org
* @author Emanuel Muckenhuber
*/
class HornetQService implements Service<HornetQServer> {
/** */
private static final String HOST = "host";
private static final String PORT = "port";
private static final String LOGGING_FACTORY = "org.jboss.as.messaging.HornetQLoggerFactory";
/**
* The name of the SocketBinding reference to use for HOST/PORT
* configuration
*/
private static final String SOCKET_REF = CommonAttributes.SOCKET_BINDING.getName();
private Configuration configuration;
private HornetQServer server;
private Map<String, SocketBinding> socketBindings = new HashMap<String, SocketBinding>();
private Map<String, OutboundSocketBinding> outboundSocketBindings = new HashMap<String, OutboundSocketBinding>();
private Map<String, SocketBinding> groupBindings = new HashMap<String, SocketBinding>();
private final InjectedValue<PathManager> pathManager = new InjectedValue<PathManager>();
private final InjectedValue<MBeanServer> mbeanServer = new InjectedValue<MBeanServer>();
private final InjectedValue<SecurityDomainContext> securityDomainContextValue = new InjectedValue<SecurityDomainContext>();
private final PathConfig pathConfig;
public HornetQService(PathConfig pathConfig) {
this.pathConfig = pathConfig;
}
Injector<PathManager> getPathManagerInjector(){
return pathManager;
}
Injector<SocketBinding> getSocketBindingInjector(String name) {
return new MapInjector<String, SocketBinding>(socketBindings, name);
}
Injector<OutboundSocketBinding> getOutboundSocketBindingInjector(String name) {
return new MapInjector<String, OutboundSocketBinding>(outboundSocketBindings, name);
}
Injector<SocketBinding> getGroupBindingInjector(String name) {
return new MapInjector<String, SocketBinding>(groupBindings, name);
}
InjectedValue<MBeanServer> getMBeanServer() {
return mbeanServer;
}
public synchronized void start(final StartContext context) throws StartException {
ClassLoader origTCCL = SecurityActions.getContextClassLoader();
// Validate whether the AIO native layer can be used
JournalType jtype = configuration.getJournalType();
if (jtype == JournalType.ASYNCIO) {
boolean supportsAIO = AIOSequentialFileFactory.isSupported();
if (supportsAIO == false) {
ROOT_LOGGER.aioWarning();
configuration.setJournalType(JournalType.NIO);
}
}
// Disable file deployment
configuration.setFileDeploymentEnabled(false);
// Setup Logging
configuration.setLogDelegateFactoryClassName(LOGGING_FACTORY);
// Setup paths
PathManager pathManager = this.pathManager.getValue();
configuration.setBindingsDirectory(pathConfig.resolveBindingsPath(pathManager));
configuration.setLargeMessagesDirectory(pathConfig.resolveLargeMessagePath(pathManager));
configuration.setJournalDirectory(pathConfig.resolveJournalPath(pathManager));
configuration.setPagingDirectory(pathConfig.resolvePagingPath(pathManager));
try {
// Update the acceptor/connector port/host values from the
// Map the socket bindings onto the connectors/acceptors
Collection<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
Collection<TransportConfiguration> connectors = configuration.getConnectorConfigurations().values();
Collection<BroadcastGroupConfiguration> broadcastGroups = configuration.getBroadcastGroupConfigurations();
Map<String, DiscoveryGroupConfiguration> discoveryGroups = configuration.getDiscoveryGroupConfigurations();
if (connectors != null) {
for (TransportConfiguration tc : connectors) {
// If there is a socket binding set the HOST/PORT values
Object socketRef = tc.getParams().remove(SOCKET_REF);
if (socketRef != null) {
String name = socketRef.toString();
String host;
int port;
OutboundSocketBinding binding = outboundSocketBindings.get(name);
if (binding == null) {
final SocketBinding socketBinding = socketBindings.get(name);
if (socketBinding == null) {
throw MESSAGES.failedToFindConnectorSocketBinding(tc.getName());
}
InetSocketAddress sa = socketBinding.getSocketAddress();
port = sa.getPort();
// resolve the host name of the address only if a loopback adress has been set
if (sa.getAddress().isLoopbackAddress()) {
host = sa.getAddress().getHostName();
} else {
host = sa.getAddress().getHostAddress();
}
} else {
port = binding.getDestinationPort();
if (binding.getDestinationAddress().isLoopbackAddress()) {
host = binding.getDestinationAddress().getHostName();
} else {
host = binding.getDestinationAddress().getHostAddress();
}
}
tc.getParams().put(HOST, host);
tc.getParams().put(PORT, String.valueOf(port));
}
}
}
if (acceptors != null) {
for (TransportConfiguration tc : acceptors) {
// If there is a socket binding set the HOST/PORT values
Object socketRef = tc.getParams().remove(SOCKET_REF);
if (socketRef != null) {
String name = socketRef.toString();
SocketBinding binding = socketBindings.get(name);
if (binding == null) {
throw MESSAGES.failedToFindConnectorSocketBinding(tc.getName());
}
InetSocketAddress socketAddress = binding.getSocketAddress();
tc.getParams().put(HOST, socketAddress.getAddress().getHostAddress());
tc.getParams().put(PORT, "" + socketAddress.getPort());
}
}
}
if(broadcastGroups != null) {
final List<BroadcastGroupConfiguration> newConfigs = new ArrayList<BroadcastGroupConfiguration>();
for(final BroadcastGroupConfiguration config : broadcastGroups) {
final String name = config.getName();
final SocketBinding binding = groupBindings.get("broadcast" + name);
if (binding == null) {
throw MESSAGES.failedToFindBroadcastSocketBinding(name);
}
newConfigs.add(BroadcastGroupAdd.createBroadcastGroupConfiguration(name, config, binding));
}
configuration.getBroadcastGroupConfigurations().clear();
configuration.getBroadcastGroupConfigurations().addAll(newConfigs);
}
if(discoveryGroups != null) {
configuration.setDiscoveryGroupConfigurations(new HashMap<String, DiscoveryGroupConfiguration>());
for(final Map.Entry<String, DiscoveryGroupConfiguration> entry : discoveryGroups.entrySet()) {
final String name = entry.getKey();
final SocketBinding binding = groupBindings.get("discovery" + name);
if (binding == null) {
throw MESSAGES.failedToFindDiscoverySocketBinding(name);
}
final DiscoveryGroupConfiguration config = DiscoveryGroupAdd.createDiscoveryGroupConfiguration(name, entry.getValue(), binding);
configuration.getDiscoveryGroupConfigurations().put(name, config);
}
}
// security
HornetQSecurityManagerAS7 hornetQSecurityManagerAS7 = new HornetQSecurityManagerAS7(securityDomainContextValue.getValue());
// Now start the server
server = new HornetQServerImpl(configuration, mbeanServer.getOptionalValue(), hornetQSecurityManagerAS7);
if (ConfigurationImpl.DEFAULT_CLUSTER_PASSWORD.equals(server.getConfiguration().getClusterPassword())) {
server.getConfiguration().setClusterPassword(java.util.UUID.randomUUID().toString());
}
// FIXME started by the JMSService
// HornetQ expects the TCCL to be set to something that can find the
// log factory class.
// ClassLoader loader = getClass().getClassLoader();
// SecurityActions.setContextClassLoader(loader);
// server.start();
} catch (Exception e) {
throw MESSAGES.failedToStartService(e);
} finally {
SecurityActions.setContextClassLoader(origTCCL);
}
}
public synchronized void stop(final StopContext context) {
try {
if (server != null) {
// FIXME stopped by the JMSService
// server.stop();
}
pathConfig.closeCallbacks(pathManager.getValue());
} catch (Exception e) {
throw MESSAGES.failedToShutdownServer(e, "HornetQ");
}
}
public synchronized HornetQServer getValue() throws IllegalStateException {
final HornetQServer server = this.server;
if (server == null) {
throw new IllegalStateException();
}
return server;
}
public Configuration getConfiguration() {
return configuration;
}
public void setConfiguration(Configuration hqConfig) {
this.configuration = hqConfig;
}
public Injector<SecurityDomainContext> getSecurityDomainContextInjector() {
return securityDomainContextValue;
}
static class PathConfig {
private final String bindingsPath;
private final String bindingsRelativeToPath;
private final String journalPath;
private final String journalRelativeToPath;
private final String largeMessagePath;
private final String largeMessageRelativeToPath;
private final String pagingPath;
private final String pagingRelativeToPath;
private final List<PathManager.Callback.Handle> callbackHandles = new ArrayList<PathManager.Callback.Handle>();
public PathConfig(String bindingsPath, String bindingsRelativeToPath, String journalPath, String journalRelativeToPath,
String largeMessagePath, String largeMessageRelativeToPath, String pagingPath, String pagingRelativeToPath) {
this.bindingsPath = bindingsPath;
this.bindingsRelativeToPath = bindingsRelativeToPath;
this.journalPath = journalPath;
this.journalRelativeToPath = journalRelativeToPath;
this.largeMessagePath = largeMessagePath;
this.largeMessageRelativeToPath = largeMessageRelativeToPath;
this.pagingPath = pagingPath;
this.pagingRelativeToPath = pagingRelativeToPath;
}
String resolveBindingsPath(PathManager pathManager) {
return resolve(pathManager, bindingsPath, bindingsRelativeToPath);
}
String resolveJournalPath(PathManager pathManager) {
return resolve(pathManager, journalPath, journalRelativeToPath);
}
String resolveLargeMessagePath(PathManager pathManager) {
return resolve(pathManager, largeMessagePath, largeMessageRelativeToPath);
}
String resolvePagingPath(PathManager pathManager) {
return resolve(pathManager, pagingPath, pagingRelativeToPath);
}
String resolve(PathManager pathManager, String path, String relativeToPath) {
return pathManager.resolveRelativePathEntry(path, relativeToPath);
}
synchronized void registerCallbacks(PathManager pathManager) {
if (bindingsRelativeToPath != null) {
callbackHandles.add(pathManager.registerCallback(bindingsRelativeToPath, PathManager.ReloadServerCallback.create(), PathManager.Event.UPDATED, PathManager.Event.REMOVED));
}
if (journalRelativeToPath != null) {
callbackHandles.add(pathManager.registerCallback(journalRelativeToPath, PathManager.ReloadServerCallback.create(), PathManager.Event.UPDATED, PathManager.Event.REMOVED));
}
if (largeMessageRelativeToPath != null) {
callbackHandles.add(pathManager.registerCallback(largeMessageRelativeToPath, PathManager.ReloadServerCallback.create(), PathManager.Event.UPDATED, PathManager.Event.REMOVED));
}
if (pagingRelativeToPath != null) {
callbackHandles.add(pathManager.registerCallback(pagingRelativeToPath, PathManager.ReloadServerCallback.create(), PathManager.Event.UPDATED, PathManager.Event.REMOVED));
}
}
synchronized void closeCallbacks(PathManager pathManager) {
for (PathManager.Callback.Handle callbackHandle : callbackHandles) {
callbackHandle.remove();
}
callbackHandles.clear();
}
}
}