// Copyright (C) 2000 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// 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 HOLDERS 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.
package net.grinder.console;
import net.grinder.common.GrinderException;
import net.grinder.communication.MessageDispatchRegistry;
import net.grinder.communication.MessageDispatchRegistry.AbstractHandler;
import net.grinder.console.common.ErrorQueue;
import net.grinder.console.common.Resources;
import net.grinder.console.communication.ConsoleCommunication;
import net.grinder.console.communication.ConsoleCommunicationImplementationEx;
import net.grinder.console.communication.DistributionControlImplementation;
import net.grinder.console.communication.ProcessControlImplementation;
import net.grinder.console.communication.server.DispatchClientCommands;
import net.grinder.console.distribution.FileDistributionImplementation;
import net.grinder.console.distribution.WireFileDistribution;
import net.grinder.console.model.*;
import net.grinder.console.synchronisation.WireDistributedBarriers;
import net.grinder.engine.console.ErrorHandlerImplementation;
import net.grinder.messages.console.RegisterExpressionViewMessage;
import net.grinder.messages.console.RegisterTestsMessage;
import net.grinder.messages.console.ReportStatisticsMessage;
import net.grinder.statistics.StatisticsServicesImplementation;
import net.grinder.util.StandardTimeAuthority;
import net.grinder.util.thread.Condition;
import org.apache.commons.lang.StringUtils;
import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.Parameter;
import org.picocontainer.behaviors.Caching;
import org.picocontainer.parameters.ComponentParameter;
import org.picocontainer.parameters.ConstantParameter;
import org.slf4j.Logger;
import java.util.Timer;
import static org.ngrinder.common.util.ExceptionUtils.processException;
import static org.ngrinder.common.util.NoOp.noOp;
/**
* Extension of {@link ConsoleFoundation} for nGrinder use.
*
* @author Grinder Developers.
* @author JunHo Yoon (modified for nGrinder)
* @since 3.0
*/
public class ConsoleFoundationEx {
private final MutablePicoContainer m_container;
private final Timer m_timer;
private boolean m_shutdown = false;
private final Condition m_eventSyncCondition;
/**
* Constructor. Allows properties to be specified.
*
* @param resources Console resources
* @param logger Logger.
* @param properties The properties.
* @param eventSyncCondition event synchronization condition.
* @exception GrinderException occurs If an error occurs.
*/
public ConsoleFoundationEx(Resources resources, Logger logger, ConsoleProperties properties,
ConsoleCommunicationSetting consoleCommunicationSetting,
Condition eventSyncCondition) throws GrinderException {
m_eventSyncCondition = eventSyncCondition;
m_container = new DefaultPicoContainer(new Caching());
m_container.addComponent(logger);
m_container.addComponent(resources);
m_container.addComponent(properties);
m_container.addComponent(StatisticsServicesImplementation.getInstance());
m_container.addComponent(new StandardTimeAuthority());
m_container.addComponent(consoleCommunicationSetting);
m_container.addComponent(SampleModelImplementationEx.class);
m_container.addComponent(SampleModelViewsImplementation.class);
m_container.addComponent(ConsoleCommunicationImplementationEx.class);
m_container.addComponent(DistributionControlImplementation.class);
m_container.addComponent(ProcessControlImplementation.class);
m_timer = new Timer(true);
m_container.addComponent(m_timer);
//noinspection RedundantArrayCreation
m_container.addComponent(FileDistributionImplementation.class, FileDistributionImplementation.class,
new Parameter[] { new ComponentParameter(DistributionControlImplementation.class),
new ComponentParameter(ProcessControlImplementation.class),
new ConstantParameter(properties.getDistributionDirectory()),
new ConstantParameter(properties.getDistributionFileFilterPattern()), });
m_container.addComponent(DispatchClientCommands.class);
m_container.addComponent(WireFileDistribution.class);
m_container.addComponent(WireMessageDispatch.class);
m_container.addComponent(WireDistributedBarriers.class);
m_container.addComponent(ErrorQueue.class);
ErrorQueue errorQueue = m_container.getComponent(ErrorQueue.class);
errorQueue.setErrorHandler(new ErrorHandlerImplementation(logger));
}
/**
* Get the component of the given type.
*
* @param <T> component type
* @param componentType component type class
* @return component
*/
public <T> T getComponent(Class<T> componentType) {
return m_container.getComponent(componentType);
}
/**
* Shut down the console.
*
*/
public void shutdown() {
m_shutdown = true;
m_container.getComponent(ConsoleCommunication.class).shutdown();
try {
m_timer.cancel();
} catch (Exception e) {
noOp();
}
if (m_container.getLifecycleState().isStarted()) {
m_container.stop();
}
}
private String getConsoleInfo() {
ConsoleProperties consoleProperties = m_container.getComponent(ConsoleProperties.class);
return StringUtils.defaultIfBlank(consoleProperties.getConsoleHost(), "localhost") + ":"
+ consoleProperties.getConsolePort();
}
/**
* Console message event loop.
*
* Dispatches communication messages appropriately. Blocks until we are {@link #shutdown()}.
*/
public void run() {
if (m_shutdown) {
throw processException("console can not run because it's shutdowned");
}
m_container.start();
ConsoleCommunication communication = m_container.getComponent(ConsoleCommunication.class);
// Need to request components, or they won't be instantiated.
m_container.getComponent(WireMessageDispatch.class);
m_container.getComponent(WireFileDistribution.class);
m_container.getComponent(WireDistributedBarriers.class);
m_container.getComponent(Logger.class).info("console {} has been started", getConsoleInfo());
synchronized (m_eventSyncCondition) {
m_eventSyncCondition.notifyAll();
}
while (communication.processOneMessage()) {
// Process until communication is shut down.
noOp();
}
}
/**
* @return the m_container
*/
public MutablePicoContainer getContainer() {
return m_container;
}
/**
* Factory that wires up the message dispatch.
*
* <p>
* Must be public for PicoContainer.
* </p>
*
* @see WireFileDistribution
*/
public static class WireMessageDispatch {
/**
* Constructor.
*
* @param communication Console communication.
* @param model Console sample model.
* @param sampleModelViews Console sample model views
* @param dispatchClientCommands Client command dispatcher.
*/
public WireMessageDispatch(ConsoleCommunication communication, final SampleModel model,
final SampleModelViews sampleModelViews, DispatchClientCommands dispatchClientCommands) {
final MessageDispatchRegistry messageDispatchRegistry = communication.getMessageDispatchRegistry();
messageDispatchRegistry.set(RegisterTestsMessage.class, new AbstractHandler<RegisterTestsMessage>() {
public void handle(RegisterTestsMessage message) {
model.registerTests(message.getTests());
}
});
messageDispatchRegistry.set(ReportStatisticsMessage.class, new AbstractHandler<ReportStatisticsMessage>() {
public void handle(ReportStatisticsMessage message) {
model.addTestReport(message.getStatisticsDelta());
}
});
messageDispatchRegistry.set(RegisterExpressionViewMessage.class,
new AbstractHandler<RegisterExpressionViewMessage>() {
public void handle(RegisterExpressionViewMessage message) {
sampleModelViews.registerStatisticExpression(message.getExpressionView());
}
});
dispatchClientCommands.registerMessageHandlers(messageDispatchRegistry);
}
}
}