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 *
040 * @see TimerListener
041 * @see org.apache.camel.management.ManagedLoadTimer
042 */
043public class TimerListenerManager extends ServiceSupport implements Runnable, CamelContextAware, StaticService {
044
045    private static final Logger LOG = LoggerFactory.getLogger(TimerListenerManager.class);
046    private final Set<TimerListener> listeners = new LinkedHashSet<TimerListener>();
047    private CamelContext camelContext;
048    private ScheduledExecutorService executorService;
049    private volatile ScheduledFuture<?> task;
050    private long interval = 1000L;
051
052    public TimerListenerManager() {
053    }
054
055    @Override
056    public void setCamelContext(CamelContext camelContext) {
057        this.camelContext = camelContext;
058    }
059
060    @Override
061    public CamelContext getCamelContext() {
062        return camelContext;
063    }
064
065    /**
066     * Gets the interval in millis.
067     * <p/>
068     * The default interval is 1000 millis.
069     *
070     * @return interval in millis.
071     */
072    public long getInterval() {
073        return interval;
074    }
075
076    /**
077     * Sets the interval in millis.
078     *
079     * @param interval interval in millis.
080     */
081    public void setInterval(long interval) {
082        this.interval = interval;
083    }
084
085    @Override
086    public void run() {
087        LOG.trace("Running scheduled TimerListener task");
088
089        if (!isRunAllowed()) {
090            LOG.debug("TimerListener task cannot run as its not allowed");
091            return;
092        }
093
094        for (TimerListener listener : listeners) {
095            try {
096                LOG.trace("Invoking onTimer on {}", listener);
097                listener.onTimer();
098            } catch (Throwable e) {
099                // ignore
100                LOG.debug("Error occurred during onTimer for TimerListener: " + listener + ". This exception will be ignored.", e);
101            }
102        }
103    }
104
105    /**
106     * Adds the listener.
107     * <p/>
108     * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
109     * to ensure that we can remove the same listener again, when invoking remove.
110     * 
111     * @param listener listener
112     */
113    public void addTimerListener(TimerListener listener) {
114        listeners.add(listener);
115        LOG.debug("Added TimerListener: {}", listener);
116    }
117
118    /**
119     * Removes the listener.
120     * <p/>
121     * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
122     * to ensure that we can remove the same listener again, when invoking remove.
123     *
124     * @param listener listener.
125     */
126    public void removeTimerListener(TimerListener listener) {
127        listeners.remove(listener);
128        LOG.debug("Removed TimerListener: {}", listener);
129    }
130
131    @Override
132    protected void doStart() throws Exception {
133        ObjectHelper.notNull(camelContext, "camelContext", this);
134
135        // create scheduled thread pool to trigger the task to run every interval
136        executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "ManagementLoadTask");
137        task = executorService.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
138        LOG.debug("Started scheduled TimerListener task to run with interval {} ms", interval);
139    }
140
141    @Override
142    protected void doStop() throws Exception {
143        // executor service will be shutdown by CamelContext
144        if (task != null) {
145            task.cancel(true);
146            task = null;
147        }
148    }
149
150    @Override
151    protected void doShutdown() throws Exception {
152        super.doShutdown();
153        // shutdown thread pool when we are shutting down
154        camelContext.getExecutorServiceManager().shutdownNow(executorService);
155        executorService = null;
156        listeners.clear();
157    }
158}
159