package nl.nn.adapterframework.configuration;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import nl.nn.adapterframework.core.IAdapter;
import nl.nn.adapterframework.util.LogUtil;
import nl.nn.adapterframework.util.RunStateEnum;
import org.apache.commons.digester.Digester;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
/**
* An implementation of {@link AdapterService} that watches a directory with XML's, every XML representing exactly one adapter.
* @author Michiel Meeuwissen
* @since 5.4
*/
public class DirectoryScanningAdapterServiceImpl extends BasicAdapterServiceImpl {
private static final Logger LOG = LogUtil.getLogger(DirectoryScanningAdapterServiceImpl.class);
private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(2);
private static final FileFilter IS_XML = new FileFilter() {
public boolean accept (File pathname){
return pathname.getName().endsWith(".xml");
}
};
private long lastScan = 0l;
private final Map<File, Collection<IAdapter>> watched = new HashMap<File, Collection<IAdapter>>();
private final File directory;
private ScheduledFuture<?> future;
private int rateInSeconds = 60;
public DirectoryScanningAdapterServiceImpl(String directory) {
this.directory = new File(directory);
schedule();
}
private void schedule() {
if (future != null) {
future.cancel(false);
}
future = EXECUTOR.scheduleAtFixedRate(new Runnable() {
public void run() {
DirectoryScanningAdapterServiceImpl.this.scan();
}
}, 0, rateInSeconds, TimeUnit.SECONDS);
}
public void setRateInSeconds(int rate) {
this.rateInSeconds = rate;
schedule();
}
@Override
public Map<String, IAdapter> getAdapters() {
if (lastScan == 0) {
synchronized(this) {
try {
wait();
} catch (InterruptedException e) {
LOG.error(e.getMessage());
}
}
}
return super.getAdapters();
}
protected synchronized void scan() {
LOG.debug("Scanning " + directory);
if (directory.exists()) {
if (directory.isDirectory()) {
File[] files = directory.listFiles(IS_XML);
{
Map<File, Collection<IAdapter>> toRemove = new HashMap<File, Collection<IAdapter>>();
toRemove.putAll(watched);
for (File file : files ) {
toRemove.remove(file);
}
for (Map.Entry<File, Collection<IAdapter>> removedAdapter : toRemove.entrySet()) {
LOG.info("File " + removedAdapter.getKey() + " not found any more, unregistering adapters" + removedAdapter.getValue());
for (IAdapter a : removedAdapter.getValue()) {
stopAndUnRegister(a);
}
watched.remove(removedAdapter.getKey());
}
}
for (File file : files) {
try {
Map<String, IAdapter> adapters = read(file.toURI().toURL());
if (adapters == null) {
LOG.warn("Could not digest " + file);
continue;
}
if (file.lastModified() > lastScan || ! watched.containsKey(file)) {
if (watched.containsKey(file)) {
for (IAdapter adapter : watched.get(file)) {
stopAndUnRegister(adapter);
}
}
for (Map.Entry<String, IAdapter> entry : adapters.entrySet()) {
if (super.getAdapters().get(entry.getValue().getName()) == null) {
registerAndStart(entry.getValue());
} else {
LOG.warn("Cannot register adapter " + entry.getValue().getName() + " because it is registered already");
}
}
watched.put(file, adapters.values());
}
} catch (MalformedURLException e) {
LOG.error(e.getMessage(), e);
} catch (ConfigurationException e) {
LOG.error(e.getMessage(), e);
} catch (SAXException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} catch (InterruptedException e) {
LOG.error(e.getMessage(), e);
}
}
} else {
LOG.warn("" + directory + " is not a directory");
}
} else {
LOG.debug("" + directory + " does not exist");
}
lastScan = System.currentTimeMillis();
notify();
}
protected void stopAndUnRegister(IAdapter adapter) {
if (adapter.getRunState() != RunStateEnum.STARTED) {
adapter.stopRunning();
}
unRegisterAdapter(adapter);
}
protected void registerAndStart(final IAdapter adapter) throws ConfigurationException {
registerAdapter(adapter);
EXECUTOR.execute(new Runnable() {
public void run() {
if (adapter.isAutoStart() && adapter.getRunState() != RunStateEnum.STARTED) {
adapter.startRunning();
}
}
});
}
synchronized Map<String, IAdapter> read(URL url) throws IOException, SAXException, InterruptedException {
try {
AdapterService catcher = new AdapterServiceImpl();
Configuration configuration = new Configuration(catcher);
ConfigurationDigester configurationDigester = new ConfigurationDigester();
Digester digester = configurationDigester.getDigester(configuration);
digester.push(catcher);
digester.parse(url.openStream());
// Does'nt work. I probably don't know how it is supposed to work.
return catcher.getAdapters();
} catch (Throwable t) {
LOG.error("For " + url + ": " + t.getMessage(), t);
return null;
}
}
}