/** * Copyright (c) 2008-2011 Sonatype, Inc. * All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions. * * This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General * Public License Version 3 as published by the Free Software Foundation. * * 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 Affero General Public License Version 3 * for more details. * * You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see * http://www.gnu.org/licenses. * * Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of * Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation. * All other trademarks are the property of their respective owners. */ package org.sonatype.nexus.events; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.AbstractLogEnabled; import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable; import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException; import org.sonatype.nexus.proxy.events.AsynchronousEventInspector; import org.sonatype.nexus.proxy.events.EventInspector; import org.sonatype.nexus.threads.NexusThreadFactory; import org.sonatype.plexus.appevents.Event; /** * A default implementation of EventInspectorHost, a component simply collecting all EventInspectors and re-emitting * events towards them in they wants to receive it. TODO: count inspector exceptions, and stop using them after some * threshold (like 3 exceptions). * * @author cstamas */ @Component( role = EventInspectorHost.class ) public class DefaultEventInspectorHost extends AbstractLogEnabled implements EventInspectorHost, Startable { @Requirement( role = EventInspector.class ) private Map<String, EventInspector> eventInspectors; private ExecutorService executor; // == Startable iface, to manage ExecutorService lifecycle public void start() throws StartingException { // set up executor executor = Executors.newCachedThreadPool( new NexusThreadFactory( "nxevthost", "Event Inspector Host" ) ); } public void stop() throws StoppingException { shutdown(); } // == public void shutdown() { // we need clean shutdown, wait all bg event inspectors to finish to have consistent state executor.shutdown(); } public boolean isCalmPeriod() { final ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor; // "calm period" is when we have no queued nor active threads return tpe.getQueue().isEmpty() && tpe.getActiveCount() == 0; } // == public void processEvent( Event<?> evt ) { for ( Map.Entry<String, EventInspector> entry : eventInspectors.entrySet() ) { EventInspector ei = entry.getValue(); EventInspectorHandler handler = new EventInspectorHandler( getLogger(), ei, evt ); // NEXUS-3800: async execution // For now, turned off. Our core is happy and snappy with it, but some of our ITs are still unprepared for // this // since they do deploy-askIndexer and usually fail, since now indexer maintenance is async! // Commenting this out all puts back into "old state". Later, we should review ITs and reenable this. // == // handler.run(); // == if ( ei instanceof AsynchronousEventInspector && executor != null && !executor.isShutdown() ) { try { executor.execute( handler ); } catch ( RejectedExecutionException e ) { // execute it in sync mode, executor is either full or shutdown (?) // in case executor is full, this "slowdown" will make it able consume and build up handler.run(); } } else { handler.run(); } } } public void onEvent( Event<?> evt ) { processEvent( evt ); } // == public static class EventInspectorHandler implements Runnable { private final Logger logger; private final EventInspector ei; private final Event<?> evt; public EventInspectorHandler( Logger logger, EventInspector ei, Event<?> evt ) { super(); this.logger = logger; this.ei = ei; this.evt = evt; } public void run() { try { if ( ei.accepts( evt ) ) { ei.inspect( evt ); } } catch ( Throwable e ) { logger.warn( "EventInspector implementation='" + ei.getClass().getName() + "' had problem inspecting an event='" + evt.getClass() + "'", e ); } } } }