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     */
017    package org.apache.camel.support;
018    
019    import java.util.LinkedHashSet;
020    import java.util.Set;
021    import java.util.concurrent.ScheduledExecutorService;
022    import java.util.concurrent.ScheduledFuture;
023    import java.util.concurrent.TimeUnit;
024    
025    import org.apache.camel.TimerListener;
026    import org.apache.camel.util.ObjectHelper;
027    import org.slf4j.Logger;
028    import org.slf4j.LoggerFactory;
029    
030    /**
031     * A {@link TimerListener} manager which triggers the
032     * {@link org.apache.camel.TimerListener} listeners once every second.
033     * <p/>
034     * The {@link #setExecutorService(java.util.concurrent.ScheduledExecutorService)} method
035     * must be invoked prior to starting this manager using the {@link #start()} method.
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     */
042    public class TimerListenerManager extends ServiceSupport implements Runnable {
043    
044        private static final Logger LOG = LoggerFactory.getLogger(TimerListenerManager.class);
045        private final Set<TimerListener> listeners = new LinkedHashSet<TimerListener>();
046        private ScheduledExecutorService executorService;
047        private volatile ScheduledFuture<?> task;
048        private long interval = 1000L;
049    
050        public TimerListenerManager() {
051        }
052    
053        public void setExecutorService(ScheduledExecutorService executorService) {
054            this.executorService = executorService;
055        }
056    
057        /**
058         * Gets the interval in millis.
059         * <p/>
060         * The default interval is 1000 millis.
061         *
062         * @return interval in millis.
063         */
064        public long getInterval() {
065            return interval;
066        }
067    
068        /**
069         * Sets the interval in millis.
070         *
071         * @param interval interval in millis.
072         */
073        public void setInterval(long interval) {
074            this.interval = interval;
075        }
076    
077        @Override
078        public void run() {
079            LOG.trace("Running scheduled TimerListener task");
080    
081            if (!isRunAllowed()) {
082                LOG.debug("TimerListener task cannot run as its not allowed");
083                return;
084            }
085    
086            for (TimerListener listener : listeners) {
087                try {
088                    LOG.trace("Invoking onTimer on {}", listener);
089                    listener.onTimer();
090                } catch (Throwable e) {
091                    // ignore
092                    LOG.debug("Error occurred during onTimer for TimerListener: " + listener + ". This exception will be ignored.", e);
093                }
094            }
095        }
096    
097        /**
098         * Adds the listener.
099         * <p/>
100         * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
101         * to ensure that we can remove the same listener again, when invoking remove.
102         * 
103         * @param listener listener
104         */
105        public void addTimerListener(TimerListener listener) {
106            listeners.add(listener);
107            LOG.debug("Added TimerListener: {}", listener);
108        }
109    
110        /**
111         * Removes the listener.
112         * <p/>
113         * It may be important to implement {@link #equals(Object)} and {@link #hashCode()} for the listener
114         * to ensure that we can remove the same listener again, when invoking remove.
115         *
116         * @param listener listener.
117         */
118        public void removeTimerListener(TimerListener listener) {
119            listeners.remove(listener);
120            LOG.debug("Removed TimerListener: {}", listener);
121        }
122    
123        @Override
124        protected void doStart() throws Exception {
125            ObjectHelper.notNull(executorService, "executorService", this);
126            task = executorService.scheduleAtFixedRate(this, 1000L, interval, TimeUnit.MILLISECONDS);
127            LOG.debug("Started scheduled TimerListener task to run with interval {} ms", interval);
128        }
129    
130        @Override
131        protected void doStop() throws Exception {
132            // executor service will be shutdown by CamelContext
133            if (task != null) {
134                task.cancel(true);
135                task = null;
136            }
137        }
138    
139        @Override
140        protected void doShutdown() throws Exception {
141            super.doShutdown();
142            listeners.clear();
143        }
144    }
145