/*
* Copyright 2003, 2004, 2005 Colin Crist
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package hermes;
import hermes.browser.HermesBrowser;
import hermes.config.DestinationConfig;
import hermes.impl.DestinationConfigKeyWrapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.jms.JMSException;
import org.apache.log4j.Logger;
/**
* @author colincrist@hermesjms.com
*/
public class HermesWatchManager
{
private static final Logger log = Logger.getLogger(HermesWatchManager.class);
public static final int DEFAULT_DEPTH_ALERT = 0 ;
public static final long DEFAULT_AGE_ALERT = 0 ;
private static class State
{
public Hermes hermes;
public DestinationConfig dConfig;
public long depth;
public Date oldest;
public Exception e;
public Map statistics;
public Collection<HermesWatchListener> listeners = new ArrayList<HermesWatchListener>();
}
private final Timer timer = new Timer();
private final Map<DestinationConfigKeyWrapper, State> watchStatistics = new HashMap<DestinationConfigKeyWrapper, State>();
private boolean keepRunning = true;
private long timeout = 30 * 1000;
private boolean updateOnNewWatchAdded = false ;
public HermesWatchManager()
{
timer.schedule(new TimerTask()
{
public void run()
{
doUpdate();
}
}, timeout, timeout);
}
public void clear()
{
synchronized (watchStatistics)
{
watchStatistics.clear();
}
}
public void updateNow()
{
timer.schedule(new TimerTask()
{
public void run()
{
doUpdate();
}
}, 0);
}
public void close()
{
clear();
keepRunning = false;
timer.cancel();
}
public void addWatch(Hermes hermes, DestinationConfig destination, HermesWatchListener listener) throws JMSException
{
if ( !keepRunning)
{
throw new HermesException("WatchManager is not running");
}
final DestinationConfig dConfig = HermesBrowser.getConfigDAO().duplicateForWatch(destination, hermes) ;
synchronized (watchStatistics)
{
final DestinationConfigKeyWrapper key = new DestinationConfigKeyWrapper(dConfig) ;
State stats;
if ( watchStatistics.containsKey(key))
{
stats = watchStatistics.get(key);
}
else
{
stats = new State();
stats.hermes = hermes;
stats.dConfig = dConfig;
watchStatistics.put(key, stats);
}
stats.listeners.add(listener);
}
if (isUpdateOnNewWatchAdded())
{
updateNow() ;
}
}
public void removeWatch(Hermes hermes, String destination, HermesWatchListener listener) throws JMSException
{
if ( !keepRunning)
{
throw new HermesException("WatchManager is not running");
}
synchronized (watchStatistics)
{
State stats;
if ( watchStatistics.containsKey(hermes.getId() + ":" + destination))
{
stats = (State) watchStatistics.get(hermes.getId() + ":" + destination);
stats.listeners.remove(listener);
if ( stats.listeners.size() == 0)
{
watchStatistics.remove(hermes.getId() + ":" + destination);
}
}
else
{
throw new HermesException("No watch exists for " + destination);
}
}
}
private void doUpdate()
{
Map<DestinationConfigKeyWrapper, State> watchStatisticsCopy = new HashMap<DestinationConfigKeyWrapper, State>();
Set<Hermes> hermesToClose = new HashSet<Hermes>();
synchronized (watchStatistics)
{
watchStatisticsCopy.putAll(watchStatistics);
}
for (Map.Entry<DestinationConfigKeyWrapper, State> entry: watchStatisticsCopy.entrySet() )
{
State stats = (State) entry.getValue();
try
{
updateWatchStatistics(stats);
}
catch (Throwable t)
{
log.error(t.getMessage(), t) ;
}
finally
{
hermesToClose.add(stats.hermes);
}
}
for (Hermes hermes : hermesToClose)
{
log.debug("closing Hermes " + hermes.getId());
try
{
hermes.close();
}
catch (JMSException e)
{
log.error(e.getMessage(), e);
}
}
}
private void updateWatchStatistics(State stats)
{
final Hermes hermes = stats.hermes;
final long previousDepth = stats.depth;
try
{
try
{
stats.depth = hermes.getDepth(stats.dConfig);
}
catch (JMSException ex)
{
log.error("cannot get depth: " + ex.getMessage());
stats.depth = 0;
}
try
{
stats.oldest = stats.depth > 0 ? new Date(hermes.getAge(stats.dConfig)) : null;
}
catch (JMSException ex)
{
log.error("cannot get oldest " + ex.getMessage());
stats.oldest = null;
}
stats.statistics = hermes.getStatistics(stats.dConfig);
if ( previousDepth != stats.depth)
{
for (final HermesWatchListener listener : stats.listeners)
{
listener.onDepthChange(hermes, stats.dConfig, stats.depth);
}
}
for (final HermesWatchListener listener : stats.listeners)
{
listener.onOldestMessageChange(hermes, stats.dConfig, stats.oldest);
}
for (final HermesWatchListener listener : stats.listeners)
{
listener.onPropertyChange(hermes, stats.dConfig, stats.statistics);
}
if ( stats.e != null)
{
stats.e = null;
for (final HermesWatchListener listener : stats.listeners)
{
listener.onException(hermes, stats.dConfig, null);
}
}
}
catch (Exception e)
{
log.error(e.getMessage(), e);
if ( stats.e == null)
{
stats.e = e;
for (final HermesWatchListener listener : stats.listeners)
{
listener.onException(hermes, stats.dConfig, e);
}
}
}
catch (Throwable t)
{
log.error(t.getMessage(), t);
}
}
public boolean isUpdateOnNewWatchAdded()
{
return updateOnNewWatchAdded;
}
public void setUpdateOnNewWatchAdded(boolean updateOnNewWatchAdded)
{
this.updateOnNewWatchAdded = updateOnNewWatchAdded;
}
}