Java tutorial
/************************************************************************* * Copyright 2009-2012 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. ************************************************************************/ package com.eucalyptus.ws; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; import com.eucalyptus.component.ComponentId; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.component.Components; import com.eucalyptus.event.EventListener; import com.eucalyptus.event.Hertz; import com.eucalyptus.event.Listeners; import org.apache.log4j.Logger; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelException; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelHandler; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.logging.Log4JLoggerFactory; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.ExternalResourceUtil; import org.jboss.netty.util.ThreadNameDeterminer; import org.jboss.netty.util.ThreadRenamingRunnable; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.bootstrap.Bootstrapper; import com.eucalyptus.bootstrap.OrderedShutdown; import com.eucalyptus.bootstrap.Provides; import com.eucalyptus.bootstrap.RunDuring; import com.eucalyptus.configurable.ConfigurableProperty; import com.eucalyptus.configurable.ConfigurablePropertyException; import com.eucalyptus.configurable.PropertyChangeListener; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.records.Logs; import com.eucalyptus.system.Threads; import com.eucalyptus.util.Cidr; import com.eucalyptus.util.CollectionUtils; import com.eucalyptus.util.Consumer; import com.eucalyptus.util.Consumers; import com.eucalyptus.util.Internets; import com.eucalyptus.util.LockResource; import com.eucalyptus.util.LogUtil; import com.eucalyptus.util.Pair; import com.eucalyptus.util.Strings; import com.google.common.base.CharMatcher; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.Future; public class WebServices { static { // Allow thread factories to determine thread naming ThreadRenamingRunnable.setThreadNameDeterminer(ThreadNameDeterminer.CURRENT); } @Provides(Empyrean.class) @RunDuring(Bootstrap.Stage.RemoteServicesInit) public static class WebServicesBootstrapper extends Bootstrapper.Simple { static { InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); } private static final Consumer<Void> registerShutdownConsumer = Consumers.once(new Consumer<Void>() { @Override public void accept(final Void aVoid) { OrderedShutdown.registerPostShutdownHook(new Runnable() { @Override public void run() { LOG.info("Releasing resources on shutdown"); try { final List<ExternalResourceReleasable> resources = Lists .<ExternalResourceReleasable>newArrayList(Handlers.pipelineExecutionHandler(), Handlers.serviceExecutionHandler()); ExternalResourceUtil .release(resources.toArray(new ExternalResourceReleasable[resources.size()])); } catch (Throwable t) { LOG.error("Error releasing resources", t); } } @Override public String toString() { return "Web services resources"; } }); } }); @Override public boolean load() throws Exception { WebServices.restart(); registerShutdownConsumer.accept(null); return true; } } @ChannelHandler.Sharable private static class ChannelGroupChannelHandler extends SimpleChannelHandler { private final ChannelGroup serverChannelGroup; public ChannelGroupChannelHandler(final ChannelGroup serverChannelGroup) { this.serverChannelGroup = serverChannelGroup; } @Override public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { serverChannelGroup.add(ctx.getChannel()); super.channelOpen(ctx, e); } } private static Iterable<Cidr> parse(final Function<String, Optional<Cidr>> cidrTransform, final String cidrList) { return Optional.presentInstances(Iterables.transform( Splitter.on(CharMatcher.anyOf(", ;:")).trimResults().omitEmptyStrings().split(cidrList), cidrTransform)); } public static class CheckCidrListPropertyChangeListener implements PropertyChangeListener { @Override public void fireChange(final ConfigurableProperty t, final Object newValue) throws ConfigurablePropertyException { if (newValue != null) try { parse(Functions.compose(CollectionUtils.<Cidr>optionalUnit(), Cidr.parseUnsafe()), Objects.toString(newValue)); } catch (IllegalArgumentException e) { throw new ConfigurablePropertyException(e.getMessage()); } } } public static class CheckNonNegativeIntegerPropertyChangeListener implements PropertyChangeListener { @Override public void fireChange(ConfigurableProperty t, Object newValue) throws ConfigurablePropertyException { int value; try { value = Integer.parseInt((String) newValue); } catch (Exception ex) { throw new ConfigurablePropertyException("Invalid value " + newValue); } if (value < 0) { throw new ConfigurablePropertyException("Invalid value " + newValue); } } } public static class CheckNonNegativeLongPropertyChangeListener implements PropertyChangeListener { @Override public void fireChange(ConfigurableProperty t, Object newValue) throws ConfigurablePropertyException { long value; try { value = Long.parseLong((String) newValue); } catch (Exception ex) { throw new ConfigurablePropertyException("Invalid value " + newValue); } if (value < 0) { throw new ConfigurablePropertyException("Invalid value " + newValue); } } } public static class CheckBooleanPropertyChangeListener implements PropertyChangeListener { @Override public void fireChange(ConfigurableProperty t, Object newValue) throws ConfigurablePropertyException { if ((newValue == null) || (!((String) newValue).equalsIgnoreCase("true") && !((String) newValue).equalsIgnoreCase("false"))) { throw new ConfigurablePropertyException("Invalid value " + newValue); } } } public static class ComponentListPropertyChangeListener implements PropertyChangeListener { private static final Predicate<String> validComponentName = new Predicate<String>() { @Override public boolean apply(@Nullable final String value) { try { ComponentIds.lookup(value); return true; } catch (NoSuchElementException e) { return false; } } }; @Override public void fireChange(ConfigurableProperty t, Object newValue) throws ConfigurablePropertyException { if (!"*".equals(String.valueOf(newValue)) && !Iterables.all(iterableFromList(String.valueOf(newValue)), validComponentName)) { throw new ConfigurablePropertyException("Invalid value " + newValue); } } } private static Logger LOG = Logger.getLogger(WebServices.class); private static Lock clientResourceLock = new ReentrantLock(); private static EventLoopGroup clientEventLoopGroup; private static Runnable serverShutdown; public static io.netty.bootstrap.Bootstrap clientBootstrap() { final EventLoopGroup clientEventLoopGroup = clientEventLoopGroup(); final io.netty.bootstrap.Bootstrap bootstrap = clientBootstrap(clientEventLoopGroup); return bootstrap; } private static io.netty.bootstrap.Bootstrap clientBootstrap(final EventLoopGroup group) { return new io.netty.bootstrap.Bootstrap().group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, StackConfiguration.CLIENT_INTERNAL_CONNECT_TIMEOUT_MILLIS); } private static EventLoopGroup clientEventLoopGroup() { if (clientEventLoopGroup != null) { return clientEventLoopGroup; } else try (final LockResource resourceLock = LockResource.lock(clientResourceLock)) { if (clientEventLoopGroup != null) { return clientEventLoopGroup; } else { final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup( StackConfiguration.CLIENT_POOL_MAX_THREADS, Threads.threadFactory("web-services-client-pool-%d")); OrderedShutdown.registerPostShutdownHook(() -> { LOG.info("Client shutdown requested"); try { final Future<?> terminationFuture = clientEventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS); terminationFuture.await(10, TimeUnit.SECONDS); if (terminationFuture.isDone()) { LOG.info("Client shutdown complete"); } else { LOG.warn("Client shutdown timed out"); } } catch (final InterruptedException e) { LOG.info("Client shutdown interrupted"); } }); return clientEventLoopGroup = eventLoopGroup; } } } public static synchronized void restart() { if (serverShutdown != null) { serverShutdown.run(); serverShutdown = null; } final Executor workerPool = workerPool(); final ChannelFactory serverChannelFactory = channelFactory(workerPool); final ChannelPipelineFactory serverPipelineFactory = Handlers.serverPipelineFactory(); final ChannelGroup serverChannelGroup = channelGroup(); final ChannelHandler channelGroupHandler = new ChannelGroupChannelHandler(serverChannelGroup); final ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = serverPipelineFactory.getPipeline(); pipeline.addLast("channel-group-handler", channelGroupHandler); return pipeline; } }; final ServerBootstrap bootstrap = serverBootstrap(serverChannelFactory, pipelineFactory); final List<Pair<InetAddress, Integer>> internalAddressAndPorts = Arrays.asList( Pair.pair(Internets.localHostInetAddress(), StackConfiguration.INTERNAL_PORT), Pair.pair(Internets.loopback(), StackConfiguration.INTERNAL_PORT)); final Set<Pair<InetAddress, Integer>> listenerAddressAndPorts = Sets.newLinkedHashSet(); listenerAddressAndPorts.addAll(internalAddressAndPorts); if (Bootstrap.isOperational()) { // skip additional listeners until bootstrapped Iterables.addAll(listenerAddressAndPorts, Iterables.transform( Iterables.filter( Iterables.concat(Collections.singleton(Internets.any()), Internets.getAllInetAddresses()), Predicates.or(parse(Cidr.parse(), StackConfiguration.LISTENER_ADDRESS_MATCH))), CollectionUtils.flipCurried(Pair.<InetAddress, Integer>pair()).apply(StackConfiguration.PORT))); } if (listenerAddressAndPorts.contains(Pair.pair(Internets.any(), StackConfiguration.INTERNAL_PORT))) { listenerAddressAndPorts.removeAll(internalAddressAndPorts); } LOG.info("Starting web services listeners on " + Joiner.on(',') .join(Iterables.transform(listenerAddressAndPorts, Pair.<InetAddress, Integer>left()))); for (final Pair<InetAddress, Integer> listenerAddressAndPort : listenerAddressAndPorts) { final InetAddress address = listenerAddressAndPort.getLeft(); final int port = listenerAddressAndPort.getRight(); try { final Channel serverChannel = bootstrap.bind(new InetSocketAddress(address, port)); serverChannelGroup.add(serverChannel); } catch (ChannelException ex) { LOG.error("Unable to bind web services listener " + address + ":" + port + ", port may be already in use."); Logs.extreme().error(ex, ex); } } try { serverShutdown = new Runnable() { AtomicBoolean ranned = new AtomicBoolean(false); @Override public void run() { if (this.ranned.compareAndSet(false, true)) { LOG.info("Server shutdown requested"); serverChannelGroup.close().awaitUninterruptibly(); serverChannelFactory.releaseExternalResources(); } else { LOG.info("Server shutdown skipped"); } } @Override public String toString() { return "Web services server shutdown"; } }; OrderedShutdown.registerPostShutdownHook(serverShutdown); } catch (Exception ex) { LOG.error(ex, ex); } } public static class WebServicePropertiesChangedEventListener implements EventListener<Hertz> { // These are all the properties in StackConfiguration that have the RestartWebServicesListener. private Integer CHANNEL_CONNECT_TIMEOUT = 500; private Boolean SERVER_CHANNEL_REUSE_ADDRESS = true; private Boolean SERVER_CHANNEL_NODELAY = true; private boolean CHANNEL_REUSE_ADDRESS = true; private Boolean CHANNEL_KEEP_ALIVE = true; private Boolean CHANNEL_NODELAY = true; private Integer SERVER_POOL_MAX_THREADS = 32; private Long SERVER_POOL_MAX_MEM_PER_CONN = 0L; private Long SERVER_POOL_TOTAL_MEM = 0L; private Long SERVER_POOL_TIMEOUT_MILLIS = 500L; private Integer SERVER_BOSS_POOL_MAX_THREADS = 128; private Long SERVER_BOSS_POOL_MAX_MEM_PER_CONN = 0L; private Long SERVER_BOSS_POOL_TOTAL_MEM = 0L; private Long SERVER_BOSS_POOL_TIMEOUT_MILLIS = 500L; private Integer PORT = 8773; private String LISTENER_ADDRESS_MATCH = "0.0.0.0"; private AtomicBoolean isRunning = new AtomicBoolean(false); public static void register() { Listeners.register(Hertz.class, new WebServicePropertiesChangedEventListener()); } @Override public void fireEvent(final Hertz event) { if (Bootstrap.isOperational() && event.isAsserted(60) && isRunning.compareAndSet(false, true)) { LOG.trace("Checking for updates to bootstrap.webservices properties"); boolean different = false; // temp vars so only look at StackConfiguration.* once (in case they change in the meantime) Integer NEW_CHANNEL_CONNECT_TIMEOUT = StackConfiguration.CHANNEL_CONNECT_TIMEOUT; Boolean NEW_SERVER_CHANNEL_REUSE_ADDRESS = StackConfiguration.SERVER_CHANNEL_REUSE_ADDRESS; Boolean NEW_SERVER_CHANNEL_NODELAY = StackConfiguration.SERVER_CHANNEL_NODELAY; boolean NEW_CHANNEL_REUSE_ADDRESS = StackConfiguration.CHANNEL_REUSE_ADDRESS; Boolean NEW_CHANNEL_KEEP_ALIVE = StackConfiguration.CHANNEL_KEEP_ALIVE; Boolean NEW_CHANNEL_NODELAY = StackConfiguration.CHANNEL_NODELAY; Integer NEW_SERVER_POOL_MAX_THREADS = StackConfiguration.SERVER_POOL_MAX_THREADS; Long NEW_SERVER_POOL_MAX_MEM_PER_CONN = StackConfiguration.SERVER_POOL_MAX_MEM_PER_CONN; Long NEW_SERVER_POOL_TOTAL_MEM = StackConfiguration.SERVER_POOL_TOTAL_MEM; Long NEW_SERVER_POOL_TIMEOUT_MILLIS = StackConfiguration.SERVER_POOL_TIMEOUT_MILLIS; Integer NEW_SERVER_BOSS_POOL_MAX_THREADS = StackConfiguration.SERVER_BOSS_POOL_MAX_THREADS; Long NEW_SERVER_BOSS_POOL_MAX_MEM_PER_CONN = StackConfiguration.SERVER_BOSS_POOL_MAX_MEM_PER_CONN; Long NEW_SERVER_BOSS_POOL_TOTAL_MEM = StackConfiguration.SERVER_BOSS_POOL_TOTAL_MEM; Long NEW_SERVER_BOSS_POOL_TIMEOUT_MILLIS = StackConfiguration.SERVER_BOSS_POOL_TIMEOUT_MILLIS; Integer NEW_PORT = StackConfiguration.PORT; String NEW_LISTENER_ADDRESS_MATCH = StackConfiguration.LISTENER_ADDRESS_MATCH; if (!CHANNEL_CONNECT_TIMEOUT.equals(NEW_CHANNEL_CONNECT_TIMEOUT)) { LOG.info("bootstrap.webservices.channel_connect_timeout has changed: oldValue = " + CHANNEL_CONNECT_TIMEOUT + ", newValue = " + NEW_CHANNEL_CONNECT_TIMEOUT); CHANNEL_CONNECT_TIMEOUT = NEW_CHANNEL_CONNECT_TIMEOUT; different = true; } if (SERVER_CHANNEL_REUSE_ADDRESS != NEW_SERVER_CHANNEL_REUSE_ADDRESS) { LOG.info("bootstrap.webservices.server_channel_reuse_address has changed: oldValue = " + SERVER_CHANNEL_REUSE_ADDRESS + ", newValue = " + NEW_SERVER_CHANNEL_REUSE_ADDRESS); SERVER_CHANNEL_REUSE_ADDRESS = NEW_SERVER_CHANNEL_REUSE_ADDRESS; different = true; } if (SERVER_CHANNEL_NODELAY != NEW_SERVER_CHANNEL_NODELAY) { LOG.info("bootstrap.webservices.server_channel_nodelay has changed: oldValue = " + SERVER_CHANNEL_NODELAY + ", newValue = " + NEW_SERVER_CHANNEL_NODELAY); SERVER_CHANNEL_NODELAY = NEW_SERVER_CHANNEL_NODELAY; different = true; } if (CHANNEL_REUSE_ADDRESS != NEW_CHANNEL_REUSE_ADDRESS) { LOG.info("bootstrap.webservices.channel_reuse_address has changed: oldValue = " + CHANNEL_REUSE_ADDRESS + ", newValue = " + NEW_CHANNEL_REUSE_ADDRESS); CHANNEL_REUSE_ADDRESS = NEW_CHANNEL_REUSE_ADDRESS; different = true; } if (CHANNEL_KEEP_ALIVE != NEW_CHANNEL_KEEP_ALIVE) { LOG.info("bootstrap.webservices.channel_keep_alive has changed: oldValue = " + CHANNEL_KEEP_ALIVE + ", newValue = " + NEW_CHANNEL_KEEP_ALIVE); CHANNEL_KEEP_ALIVE = NEW_CHANNEL_KEEP_ALIVE; different = true; } if (CHANNEL_NODELAY != NEW_CHANNEL_NODELAY) { LOG.info("bootstrap.webservices.channel_nodelay has changed: oldValue = " + CHANNEL_NODELAY + ", newValue = " + NEW_CHANNEL_NODELAY); CHANNEL_NODELAY = NEW_CHANNEL_NODELAY; different = true; } if (!SERVER_POOL_MAX_THREADS.equals(NEW_SERVER_POOL_MAX_THREADS)) { LOG.info("bootstrap.webservices.server_pool_max_threads has changed: oldValue = " + SERVER_POOL_MAX_THREADS + ", newValue = " + NEW_SERVER_POOL_MAX_THREADS); SERVER_POOL_MAX_THREADS = NEW_SERVER_POOL_MAX_THREADS; different = true; } if (!SERVER_POOL_MAX_MEM_PER_CONN.equals(NEW_SERVER_POOL_MAX_MEM_PER_CONN)) { LOG.info("bootstrap.webservices.server_pool_max_mem_per_conn has changed: oldValue = " + SERVER_POOL_MAX_MEM_PER_CONN + ", newValue = " + NEW_SERVER_POOL_MAX_MEM_PER_CONN); SERVER_POOL_MAX_MEM_PER_CONN = NEW_SERVER_POOL_MAX_MEM_PER_CONN; different = true; } if (!SERVER_POOL_TOTAL_MEM.equals(NEW_SERVER_POOL_TOTAL_MEM)) { LOG.info("bootstrap.webservices.server_pool_total_mem has changed: oldValue = " + SERVER_POOL_TOTAL_MEM + ", newValue = " + NEW_SERVER_POOL_TOTAL_MEM); SERVER_POOL_TOTAL_MEM = NEW_SERVER_POOL_TOTAL_MEM; different = true; } if (!SERVER_POOL_TIMEOUT_MILLIS.equals(NEW_SERVER_POOL_TIMEOUT_MILLIS)) { LOG.info("bootstrap.webservices.server_pool_timeout_millis has changed: oldValue = " + SERVER_POOL_TIMEOUT_MILLIS + ", newValue = " + NEW_SERVER_POOL_TIMEOUT_MILLIS); SERVER_POOL_TIMEOUT_MILLIS = NEW_SERVER_POOL_TIMEOUT_MILLIS; different = true; } if (!SERVER_BOSS_POOL_MAX_THREADS.equals(NEW_SERVER_BOSS_POOL_MAX_THREADS)) { LOG.info("bootstrap.webservices.server_boss_pool_max_threads has changed: oldValue = " + SERVER_BOSS_POOL_MAX_THREADS + ", newValue = " + NEW_SERVER_BOSS_POOL_MAX_THREADS); SERVER_BOSS_POOL_MAX_THREADS = NEW_SERVER_BOSS_POOL_MAX_THREADS; different = true; } if (!SERVER_BOSS_POOL_MAX_MEM_PER_CONN.equals(NEW_SERVER_BOSS_POOL_MAX_MEM_PER_CONN)) { LOG.info("bootstrap.webservices.server_boss_pool_max_mem_per_conn has changed: oldValue = " + SERVER_BOSS_POOL_MAX_MEM_PER_CONN + ", newValue = " + NEW_SERVER_BOSS_POOL_MAX_MEM_PER_CONN); SERVER_BOSS_POOL_MAX_MEM_PER_CONN = NEW_SERVER_BOSS_POOL_MAX_MEM_PER_CONN; different = true; } if (!SERVER_BOSS_POOL_TOTAL_MEM.equals(NEW_SERVER_BOSS_POOL_TOTAL_MEM)) { LOG.info("bootstrap.webservices.server_boss_pool_total_mem has changed: oldValue = " + SERVER_BOSS_POOL_TOTAL_MEM + ", newValue = " + NEW_SERVER_BOSS_POOL_TOTAL_MEM); SERVER_BOSS_POOL_TOTAL_MEM = NEW_SERVER_BOSS_POOL_TOTAL_MEM; different = true; } if (!SERVER_BOSS_POOL_TIMEOUT_MILLIS.equals(NEW_SERVER_BOSS_POOL_TIMEOUT_MILLIS)) { LOG.info("bootstrap.webservices.server_boss_pool_timeout_millis has changed: oldValue = " + SERVER_BOSS_POOL_TIMEOUT_MILLIS + ", newValue = " + NEW_SERVER_BOSS_POOL_TIMEOUT_MILLIS); SERVER_BOSS_POOL_TIMEOUT_MILLIS = NEW_SERVER_BOSS_POOL_TIMEOUT_MILLIS; different = true; } if (!PORT.equals(NEW_PORT)) { LOG.info("bootstrap.webservices.port has changed: oldValue = " + PORT + ", newValue = " + NEW_PORT); PORT = NEW_PORT; different = true; } if (!LISTENER_ADDRESS_MATCH.equals(NEW_LISTENER_ADDRESS_MATCH)) { LOG.info("bootstrap.webservices.listener_address_match has changed: oldValue = " + LISTENER_ADDRESS_MATCH + ", newValue = " + NEW_LISTENER_ADDRESS_MATCH); LISTENER_ADDRESS_MATCH = NEW_LISTENER_ADDRESS_MATCH; different = true; } if (different) { LOG.info( "One or more bootstrap.webservices properties have changed, restarting web services listeners [May change ports]"); new Thread(Threads.threadUniqueName("web-services-restarter")) { public void run() { try { restart(); LOG.info("Web services restart complete"); } catch (Exception ex) { LOG.error(ex, ex); } finally { isRunning.set(false); } } }.start(); } else { isRunning.set(false); LOG.trace("No updates found to web services properties"); } } } } private static DefaultChannelGroup channelGroup() { return new DefaultChannelGroup(Empyrean.INSTANCE.getFullName() + ":" + WebServices.class.getSimpleName() + ":" + StackConfiguration.PORT); } private static ServerBootstrap serverBootstrap(final ChannelFactory channelFactory, ChannelPipelineFactory serverPipelineFactory) { final ServerBootstrap bootstrap = new ServerBootstrap(channelFactory); bootstrap.setPipelineFactory(serverPipelineFactory); if (!Logs.isExtrrreeeme()) { LOG.info("Creating server bootstrap. (log level EXTREME for details)"); } else { LOG.trace(LogUtil.subheader("Creating server boss thread pool.")); LOG.trace(String.format("-> Server option: %25.25s = %s", "child.tcpNoDelay", StackConfiguration.CHANNEL_NODELAY)); LOG.trace(String.format("-> Server option: %25.25s = %s", "child.keepAlive", StackConfiguration.CHANNEL_KEEP_ALIVE)); LOG.trace(String.format("-> Server option: %25.25s = %s", "child.reuseAddress", StackConfiguration.CHANNEL_REUSE_ADDRESS)); LOG.trace(String.format("-> Server option: %25.25s = %s", "child.connectTimeoutMillis", StackConfiguration.CHANNEL_CONNECT_TIMEOUT)); LOG.trace(String.format("-> Server option: %25.25s = %s", "tcpNoDelay", StackConfiguration.SERVER_CHANNEL_NODELAY)); LOG.trace(String.format("-> Server option: %25.25s = %s", "reuseAddress", StackConfiguration.SERVER_CHANNEL_REUSE_ADDRESS)); } bootstrap.setOption("child.tcpNoDelay", StackConfiguration.CHANNEL_NODELAY); bootstrap.setOption("child.keepAlive", StackConfiguration.CHANNEL_KEEP_ALIVE); bootstrap.setOption("child.reuseAddress", StackConfiguration.CHANNEL_REUSE_ADDRESS); bootstrap.setOption("child.connectTimeoutMillis", StackConfiguration.CHANNEL_CONNECT_TIMEOUT); bootstrap.setOption("tcpNoDelay", StackConfiguration.SERVER_CHANNEL_NODELAY); bootstrap.setOption("reuseAddress", StackConfiguration.SERVER_CHANNEL_REUSE_ADDRESS); return bootstrap; } private static NioServerSocketChannelFactory channelFactory(final Executor workerPool) { return new NioServerSocketChannelFactory( Executors.newCachedThreadPool(Threads.threadFactory("web-services-boss-pool-%d")), workerPool, StackConfiguration.SERVER_POOL_MAX_THREADS); } private static Executor workerPool() { if (!Logs.isExtrrreeeme()) { LOG.info(String.format("Creating server worker thread pool (%d). (log level EXTREME for details)", StackConfiguration.SERVER_POOL_MAX_THREADS)); } else { LOG.trace(LogUtil.subheader("Creating server worker thread pool.")); LOG.trace( String.format("-> Pool threads: %8d", StackConfiguration.SERVER_POOL_MAX_THREADS)); LOG.trace(String.format("-> Pool timeout: %8d ms", StackConfiguration.SERVER_POOL_TIMEOUT_MILLIS)); LOG.trace(String.format("-> Max memory per connection: %8.2f MB", StackConfiguration.SERVER_POOL_MAX_MEM_PER_CONN / (1024f * 1024f))); LOG.trace(String.format("-> Max total memory: %8.2f MB", StackConfiguration.SERVER_POOL_TOTAL_MEM / (1024f * 1024f))); } final Executor workerPool = new OrderedMemoryAwareThreadPoolExecutor( StackConfiguration.SERVER_POOL_MAX_THREADS, StackConfiguration.SERVER_POOL_MAX_MEM_PER_CONN, StackConfiguration.SERVER_POOL_TOTAL_MEM, StackConfiguration.SERVER_POOL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS, Threads.threadFactory("web-services-worker-pool-%d")); return workerPool; } private static Iterable<String> iterableFromList(final String list) { return Splitter.on(CharMatcher.anyOf(" ,\t\n\r")).omitEmptyStrings().trimResults().split(list); } public static boolean isSoapEnabled(final Class<? extends ComponentId> component) { return !StackConfiguration.DISABLED_SOAP_API_COMPONENTS.equals("*") && !Iterables.contains(Iterables .transform(iterableFromList(StackConfiguration.DISABLED_SOAP_API_COMPONENTS), Strings.lower()), Components.lookup(component).getName()); } }