001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.support;
018
019import java.util.LinkedHashSet;
020import java.util.Set;
021import java.util.concurrent.ScheduledExecutorService;
022import java.util.concurrent.ScheduledFuture;
023import java.util.concurrent.TimeUnit;
024
025import org.apache.camel.CamelContext;
026import org.apache.camel.CamelContextAware;
027import org.apache.camel.StaticService;
028import org.apache.camel.TimerListener;
029import org.apache.camel.util.ObjectHelper;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * A {@link TimerListener} manager which triggers the
035 * {@link org.apache.camel.TimerListener} listeners once every second.
036 * <p/>
037 * Also ensure when adding and remove listeners, that they are correctly removed to avoid
038 * leaking memory.
039 * <p/>
040 * From Camel 2.13 onwards the {@link TimerListenerManager} is only enabled if
041 * {@link org.apache.camel.spi.ManagementStrategy#isLoadStatisticsEnabled()} is enabled.
042 *
043 * @see TimerListener
044 */
045public class TimerListenerManager extends ServiceSupport implements Runnable, CamelContextAware, StaticService {
046
047    private static final Logger LOG = LoggerFactory.getLogger(TimerListenerManager.class);
048    private final Set<TimerListener> listeners = new LinkedHashSet<TimerListener>();
049    private CamelContext camelContext;
050    private ScheduledExecutorService executorService;
051    private volatile ScheduledFuture<?> task;
052    private long interval = 1000L;
053
054    public TimerListenerManager() {
055    }
056
057    @Override
058    public void setCamelContext(CamelContext camelContext) {
059        this.camelContext = camelContext;
060    }
061
062    @Override
063    public CamelContext getCamelContext() {
064        return camelContext;
065    }
066
067    /**
068     * Gets the interval in millis.
069     * <p/>
070     * The default interval is 1000 millis.
071     *
072     * @return interval in millis.
073     */
074    public long getInterval() {
075        return interval;
076    }
077
078    /**
079     * Sets the interval in millis.
080     *
081     * @param interval interval in millis.
082     */
083    public void setInterval(long interval) {
084        this.interval = interval;
085    }
086
087    @Override
088    public void run() {
089        LOG.trace("Running scheduled TimerListener task");
090
091        if (!isRunAllowed()) {
092            LOG.debug("TimerListener task cannot run as its not allowed");
093            return;
094        }
095
096        for (TimerListener listener : listeners) {
097            try {
098                LOG.trace("Invoking onTimer on {}", listener);
099                listener.onTimer();
100            } catch (Throwable e) {
101                // ignore
102                LOG.debug("Error occurred during onTimer for TimerListener: " + listener + ". This exception will be ignored.", e);
103            }
104        }
105    }
106
107    /**
108     * Adds the listener.
109     * <p/>
110     * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
111     * to ensure that we can remove the same listener again, when invoking remove.
112     * 
113     * @param listener listener
114     */
115    public void addTimerListener(TimerListener listener) {
116        listeners.add(listener);
117        LOG.debug("Added TimerListener: {}", listener);
118    }
119
120    /**
121     * Removes the listener.
122     * <p/>
123     * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
124     * to ensure that we can remove the same listener again, when invoking remove.
125     *
126     * @param listener listener.
127     */
128    public void removeTimerListener(TimerListener listener) {
129        listeners.remove(listener);
130        LOG.debug("Removed TimerListener: {}", listener);
131    }
132
133    @Override
134    protected void doStart() throws Exception {
135        ObjectHelper.notNull(camelContext, "camelContext", this);
136
137        // create scheduled thread pool to trigger the task to run every interval
138        executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "ManagementLoadTask");
139        task = executorService.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
140        LOG.debug("Started scheduled TimerListener task to run with interval {} ms", interval);
141    }
142
143    @Override
144    protected void doStop() throws Exception {
145        // executor service will be shutdown by CamelContext
146        if (task != null) {
147            task.cancel(true);
148            task = null;
149        }
150    }
151
152    @Override
153    protected void doShutdown() throws Exception {
154        super.doShutdown();
155        // shutdown thread pool when we are shutting down
156        camelContext.getExecutorServiceManager().shutdownNow(executorService);
157        executorService = null;
158        listeners.clear();
159    }
160}
161