Java tutorial
/* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.wso2.andes.server.virtualhost; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.wso2.andes.AMQException; import org.wso2.andes.AMQStoreException; import org.wso2.andes.amqp.QpidAndesBridge; import org.wso2.andes.configuration.qpid.*; import org.wso2.andes.framing.AMQShortString; import org.wso2.andes.framing.FieldTable; import org.wso2.andes.kernel.*; import org.wso2.andes.server.AMQBrokerManagerMBean; import org.wso2.andes.server.binding.BindingFactory; import org.wso2.andes.server.connection.ConnectionRegistry; import org.wso2.andes.server.connection.IConnectionRegistry; import org.wso2.andes.server.exchange.*; import org.wso2.andes.server.federation.BrokerLink; import org.wso2.andes.server.information.management.QueueManagementInformationMBean; import org.wso2.andes.server.logging.LogSubject; import org.wso2.andes.server.logging.actors.CurrentActor; import org.wso2.andes.server.logging.messages.VirtualHostMessages; import org.wso2.andes.server.logging.subjects.MessageStoreLogSubject; import org.wso2.andes.server.management.AMQManagedObject; import org.wso2.andes.server.management.ManagedObject; import org.wso2.andes.server.protocol.AMQConnectionModel; import org.wso2.andes.server.protocol.AMQSessionModel; import org.wso2.andes.server.queue.AMQQueue; import org.wso2.andes.server.queue.AMQQueueFactory; import org.wso2.andes.server.queue.DefaultQueueRegistry; import org.wso2.andes.server.queue.QueueRegistry; import org.wso2.andes.server.registry.ApplicationRegistry; import org.wso2.andes.server.registry.IApplicationRegistry; import org.wso2.andes.server.security.SecurityManager; import org.wso2.andes.server.security.auth.manager.AuthenticationManager; import org.wso2.andes.server.stats.StatisticsCounter; import org.wso2.andes.server.store.*; import org.wso2.andes.server.store.MessageStore; import org.wso2.andes.server.virtualhost.plugins.VirtualHostPlugin; import org.wso2.andes.server.virtualhost.plugins.VirtualHostPluginFactory; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class VirtualHostImpl implements VirtualHost { private static final Logger log = Logger.getLogger(VirtualHostImpl.class); private final String name; private ConnectionRegistry connectionRegistry; private QueueRegistry queueRegistry; private ExchangeRegistry exchangeRegistry; private ExchangeFactory exchangeFactory; private MessageStore messageStore; protected VirtualHostMBean virtualHostMBean; private AMQBrokerManagerMBean brokerMBean; private QueueManagementInformationMBean queueManagementInformationMBean; private final AuthenticationManager authenticationManager; private SecurityManager securityManager; private final ScheduledThreadPoolExecutor houseKeepingTasks; private final IApplicationRegistry appRegistry; private VirtualHostConfiguration configuration; private DurableConfigurationStore durableConfigurationStore; private BindingFactory bindingFactory; private BrokerConfig broker; private UUID id; private boolean statisticsEnabled = false; private StatisticsCounter messagesDelivered, dataDelivered, messagesReceived, dataReceived; private final long createTime = System.currentTimeMillis(); private final ConcurrentHashMap<BrokerLink, BrokerLink> links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; public IConnectionRegistry getConnectionRegistry() { return connectionRegistry; } public VirtualHostConfiguration getConfiguration() { return configuration; } public UUID getId() { return id; } public VirtualHostConfigType getConfigType() { return VirtualHostConfigType.getInstance(); } public ConfiguredObject getParent() { return getBroker(); } public boolean isDurable() { return false; } /** * Virtual host JMX MBean class. * <p/> * This has some of the methods implemented from management intrerface for exchanges. Any * implementaion of an Exchange MBean should extend this class. */ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost { public VirtualHostMBean() throws NotCompliantMBeanException { super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); } public String getObjectInstanceName() { return ObjectName.quote(name); } public String getName() { return name; } public VirtualHostImpl getVirtualHost() { return VirtualHostImpl.this; } } public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig) throws Exception { this(appRegistry, hostConfig, null); } public VirtualHostImpl(VirtualHostConfiguration hostConfig, MessageStore store) throws Exception { this(ApplicationRegistry.getInstance(), hostConfig, store); } private VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception { if (hostConfig == null) { throw new IllegalAccessException("HostConfig and MessageStore cannot be null"); } this.appRegistry = appRegistry; broker = this.appRegistry.getBroker(); configuration = hostConfig; name = configuration.getName(); id = this.appRegistry.getConfigStore().createId(); CurrentActor.get().message(VirtualHostMessages.CREATED(name)); if (name == null || name.length() == 0) { throw new IllegalArgumentException("Illegal name (" + name + ") for virtualhost."); } securityManager = new SecurityManager(this.appRegistry.getSecurityManager()); securityManager.configureHostPlugins(configuration); virtualHostMBean = new VirtualHostMBean(); connectionRegistry = new ConnectionRegistry(); houseKeepingTasks = new ScheduledThreadPoolExecutor(configuration.getHouseKeepingThreadCount()); queueRegistry = new DefaultQueueRegistry(this); exchangeFactory = new DefaultExchangeFactory(this); exchangeFactory.initialise(configuration); StartupRoutingTable configFileRT = new StartupRoutingTable(); durableConfigurationStore = configFileRT; if (store != null) { messageStore = store; durableConfigurationStore = store; } else { initialiseAndesStores(hostConfig); } AndesKernelBoot.startAndesCluster(); exchangeRegistry = new DefaultExchangeRegistry(this); bindingFactory = new BindingFactory(this); // This needs to be after the RT has been defined as it creates the default durable exchanges. initialiseModel(configuration); exchangeRegistry.initialise(); authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(); brokerMBean = new AMQBrokerManagerMBean(virtualHostMBean); brokerMBean.register(); queueManagementInformationMBean = new QueueManagementInformationMBean(virtualHostMBean); queueManagementInformationMBean.register(); initialiseHouseKeeping(hostConfig.getHousekeepingExpiredMessageCheckPeriod()); initialiseStatistics(); } private void initialiseHouseKeeping(long period) { /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */ if (period != 0L) { class ExpiredMessagesTask extends HouseKeepingTask { public ExpiredMessagesTask(VirtualHost vhost) { super(vhost); } public void execute() { for (AMQQueue q : queueRegistry.getQueues()) { if (log.isDebugEnabled()) { log.debug("Checking message status for queue: " + q.getName()); } try { q.checkMessageStatus(); } catch (Exception e) { log.error("Exception in housekeeping for queue: " + q.getNameShortString().toString(), e); //Don't throw exceptions as this will stop the // house keeping task from running. } } for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) { if (log.isDebugEnabled()) { log.debug("Checking for long running open transactions on connection " + connection); } for (AMQSessionModel session : connection.getSessionModels()) { if (log.isDebugEnabled()) { log.debug("Checking for long running open transactions on session " + session); } try { session.checkTransactionStatus(configuration.getTransactionTimeoutOpenWarn(), configuration.getTransactionTimeoutOpenClose(), configuration.getTransactionTimeoutIdleWarn(), configuration.getTransactionTimeoutIdleClose()); } catch (Exception e) { log.error("Exception in housekeeping for connection: " + connection.toString(), e); } } } } } scheduleHouseKeepingTask(period, new ExpiredMessagesTask(this)); Map<String, VirtualHostPluginFactory> plugins = ApplicationRegistry.getInstance().getPluginManager() .getVirtualHostPlugins(); if (plugins != null) { for (Map.Entry<String, VirtualHostPluginFactory> entry : plugins.entrySet()) { String pluginName = entry.getKey(); VirtualHostPluginFactory factory = entry.getValue(); try { VirtualHostPlugin plugin = factory.newInstance(this); // If we had configuration for the plugin the schedule it. if (plugin != null) { houseKeepingTasks.scheduleAtFixedRate(plugin, plugin.getDelay() / 2, plugin.getDelay(), plugin.getTimeUnit()); log.info("Loaded VirtualHostPlugin:" + plugin); } } catch (RuntimeException e) { log.error("Unable to load VirtualHostPlugin:" + pluginName + " due to:" + e.getMessage(), e); } } } } } /** * Allow other broker components to register a HouseKeepingTask * * @param period How often this task should run, in ms. * @param task The task to run. */ public void scheduleHouseKeepingTask(long period, HouseKeepingTask task) { houseKeepingTasks.scheduleAtFixedRate(task, period / 2, period, TimeUnit.MILLISECONDS); } public long getHouseKeepingTaskCount() { return houseKeepingTasks.getTaskCount(); } public long getHouseKeepingCompletedTaskCount() { return houseKeepingTasks.getCompletedTaskCount(); } public int getHouseKeepingPoolSize() { return houseKeepingTasks.getCorePoolSize(); } public void setHouseKeepingPoolSize(int newSize) { houseKeepingTasks.setCorePoolSize(newSize); } public int getHouseKeepingActiveCount() { return houseKeepingTasks.getActiveCount(); } /** * Initialize the message store * * @param hostConfig VirtualHost Configuration * @throws Exception */ private void initialiseAndesStores(VirtualHostConfiguration hostConfig) throws Exception { //Set virtual host AndesKernelBoot.initVirtualHostConfigSynchronizer(this); //kernel will start message stores for Andes AndesKernelBoot.startAndesStores(); // this is considered as an internal impl now, so hard coding // qpid related messagestore MessageStore messageStore = new QpidDeprecatedMessageStore(); VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(this, messageStore); messageStore.configureConfigStore(this.getName(), recoveryHandler, hostConfig.getStoreConfiguration(), storeLogSubject); messageStore.configureMessageStore(this.getName(), recoveryHandler, hostConfig.getStoreConfiguration(), storeLogSubject); messageStore.configureTransactionLog(this.getName(), recoveryHandler, hostConfig.getStoreConfiguration(), storeLogSubject); this.messageStore = messageStore; durableConfigurationStore = messageStore; } private void initialiseModel(VirtualHostConfiguration config) throws ConfigurationException, AMQException { if (log.isDebugEnabled()) { log.debug("Loading configuration for virtualhost: " + config.getName()); } List exchangeNames = config.getExchanges(); for (Object exchangeNameObj : exchangeNames) { String exchangeName = String.valueOf(exchangeNameObj); configureExchange(config.getExchangeConfiguration(exchangeName)); } String[] queueNames = config.getQueueNames(); for (Object queueNameObj : queueNames) { String queueName = String.valueOf(queueNameObj); configureQueue(config.getQueueConfiguration(queueName)); } } private void configureExchange(ExchangeConfiguration exchangeConfiguration) throws AMQException { AMQShortString exchangeName = new AMQShortString(exchangeConfiguration.getName()); Exchange exchange; exchange = exchangeRegistry.getExchange(exchangeName); if (exchange == null) { AMQShortString type = new AMQShortString(exchangeConfiguration.getType()); boolean durable = exchangeConfiguration.getDurable(); boolean autodelete = exchangeConfiguration.getAutoDelete(); Exchange newExchange = exchangeFactory.createExchange(exchangeName, type, durable, autodelete, 0); exchangeRegistry.registerExchange(newExchange); if (newExchange.isDurable()) { durableConfigurationStore.createExchange(newExchange); //tell Andes kernel to create Exchange QpidAndesBridge.createExchange(newExchange); } } } private void configureQueue(QueueConfiguration queueConfiguration) throws AMQException, ConfigurationException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); if (queue.isDurable()) { getDurableConfigurationStore().createQueue(queue); } //tell Andes kernel to create queue QpidAndesBridge.createQueue(queue); String exchangeName = queueConfiguration.getExchange(); Exchange exchange = exchangeRegistry .getExchange(exchangeName == null ? null : new AMQShortString(exchangeName)); if (exchange == null) { exchange = exchangeRegistry.getDefaultExchange(); } if (exchange == null) { throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + exchangeName); } List routingKeys = queueConfiguration.getRoutingKeys(); if (routingKeys == null || routingKeys.isEmpty()) { routingKeys = Collections.singletonList(queue.getNameShortString()); } for (Object routingKeyNameObj : routingKeys) { AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj)); if (log.isInfoEnabled()) { log.info("Binding queue:" + queue + " with routing key '" + routingKey + "' to exchange:" + this); } bindingFactory.addBinding(routingKey.toString(), queue, exchange, null); } if (exchange != exchangeRegistry.getDefaultExchange()) { bindingFactory.addBinding(queue.getNameShortString().toString(), queue, exchange, null); } } public String getName() { return name; } public BrokerConfig getBroker() { return broker; } public String getFederationTag() { return broker.getFederationTag(); } public void setBroker(final BrokerConfig broker) { this.broker = broker; } public long getCreateTime() { return createTime; } public QueueRegistry getQueueRegistry() { return queueRegistry; } public ExchangeRegistry getExchangeRegistry() { return exchangeRegistry; } public ExchangeFactory getExchangeFactory() { return exchangeFactory; } public MessageStore getMessageStore() { return messageStore; } public TransactionLog getTransactionLog() { return messageStore; } public DurableConfigurationStore getDurableConfigurationStore() { return durableConfigurationStore; } public AuthenticationManager getAuthenticationManager() { return authenticationManager; } public SecurityManager getSecurityManager() { return securityManager; } public void close() { //Stop Connections connectionRegistry.close(); //Stop the Queues processing if (queueRegistry != null) { for (AMQQueue queue : queueRegistry.getQueues()) { queue.stop(); } } //Stop Housekeeping if (houseKeepingTasks != null) { houseKeepingTasks.shutdown(); try { if (!houseKeepingTasks.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) { houseKeepingTasks.shutdownNow(); } } catch (InterruptedException e) { log.warn("Interrupted during Housekeeping shutdown:" + e.getMessage()); // Swallowing InterruptedException ok as we are shutting down. } } //Close MessageStore if (messageStore != null) { //Remove MessageStore Interface should not throw Exception try { messageStore.close(); } catch (Exception e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } CurrentActor.get().message(VirtualHostMessages.CLOSED()); } public ManagedObject getBrokerMBean() { return brokerMBean; } public ManagedObject getManagedObject() { return virtualHostMBean; } public UUID getBrokerId() { return appRegistry.getBrokerId(); } public IApplicationRegistry getApplicationRegistry() { return appRegistry; } public BindingFactory getBindingFactory() { return bindingFactory; } public void registerMessageDelivered(long messageSize) { if (isStatisticsEnabled()) { messagesDelivered.registerEvent(1L); dataDelivered.registerEvent(messageSize); } appRegistry.registerMessageDelivered(messageSize); } public void registerMessageReceived(long messageSize, long timestamp) { if (isStatisticsEnabled()) { messagesReceived.registerEvent(1L, timestamp); dataReceived.registerEvent(messageSize, timestamp); } appRegistry.registerMessageReceived(messageSize, timestamp); } public StatisticsCounter getMessageReceiptStatistics() { return messagesReceived; } public StatisticsCounter getDataReceiptStatistics() { return dataReceived; } public StatisticsCounter getMessageDeliveryStatistics() { return messagesDelivered; } public StatisticsCounter getDataDeliveryStatistics() { return dataDelivered; } public void resetStatistics() { messagesDelivered.reset(); dataDelivered.reset(); messagesReceived.reset(); dataReceived.reset(); for (AMQConnectionModel connection : connectionRegistry.getConnections()) { connection.resetStatistics(); } } public void initialiseStatistics() { setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && appRegistry.getConfiguration().isStatisticsGenerationVirtualhostsEnabled()); messagesDelivered = new StatisticsCounter("messages-delivered-" + getName()); dataDelivered = new StatisticsCounter("bytes-delivered-" + getName()); messagesReceived = new StatisticsCounter("messages-received-" + getName()); dataReceived = new StatisticsCounter("bytes-received-" + getName()); } public boolean isStatisticsEnabled() { return statisticsEnabled; } public void setStatisticsEnabled(boolean enabled) { statisticsEnabled = enabled; } public void createBrokerConnection(final String transport, final String host, final int port, final String vhost, final boolean durable, final String authMechanism, final String username, final String password) { BrokerLink blink = new BrokerLink(this, transport, host, port, vhost, durable, authMechanism, username, password); if (links.putIfAbsent(blink, blink) != null) { getConfigStore().addConfiguredObject(blink); } } public void removeBrokerConnection(final String transport, final String host, final int port, final String vhost) { removeBrokerConnection(new BrokerLink(this, transport, host, port, vhost, false, null, null, null)); } public void removeBrokerConnection(BrokerLink blink) { blink = links.get(blink); if (blink != null) { blink.close(); getConfigStore().removeConfiguredObject(blink); } } public ConfigStore getConfigStore() { return getApplicationRegistry().getConfigStore(); } /** * Temporary Startup RT class to record the creation of persistent queues / exchanges. * <p/> * <p/> * This is so we can replay the creation of queues/exchanges in to the real _RT after it has been loaded. * This should be removed after the _RT has been fully split from the the TL */ private static class StartupRoutingTable implements DurableConfigurationStore { public List<Exchange> exchange = new LinkedList<Exchange>(); public List<CreateQueueTuple> queue = new LinkedList<CreateQueueTuple>(); public List<CreateBindingTuple> bindings = new LinkedList<CreateBindingTuple>(); public void configure(VirtualHost virtualHost, String base, VirtualHostConfiguration config) throws Exception { } public void close() throws Exception { } public void removeMessage(Long messageId) throws AMQException { //To change body of implemented methods use File | Settings | File Templates. } public void configureConfigStore(String name, ConfigurationRecoveryHandler recoveryHandler, Configuration config, LogSubject logSubject) throws Exception { //To change body of implemented methods use File | Settings | File Templates. } public void createExchange(Exchange exchange) throws AMQStoreException { if (exchange.isDurable()) { this.exchange.add(exchange); } } public void removeExchange(Exchange exchange) throws AMQStoreException { } public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException { if (exchange.isDurable() && queue.isDurable()) { bindings.add(new CreateBindingTuple(exchange, routingKey, queue, args)); } } public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException { } public void createQueue(AMQQueue queue) throws AMQStoreException { createQueue(queue, null); } public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException { if (queue.isDurable()) { this.queue.add(new CreateQueueTuple(queue, arguments)); } } public void removeQueue(AMQQueue queue) throws AMQStoreException { } private static class CreateQueueTuple { public AMQQueue queue; public FieldTable arguments; public CreateQueueTuple(AMQQueue queue, FieldTable arguments) { this.queue = queue; this.arguments = arguments; } } private static class CreateBindingTuple { public AMQQueue queue; public FieldTable arguments; public Exchange exchange; public AMQShortString routingKey; public CreateBindingTuple(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) { this.exchange = exchange; this.routingKey = routingKey; this.queue = queue; arguments = args; } } public void updateQueue(AMQQueue queue) throws AMQStoreException { } } @Override public String toString() { return name; } }