/*******************************************************************************
* Copyright (c) 2014 MEDEVIT.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* T. Huster - initial API and implementation
*******************************************************************************/
package at.medevit.elexis.ehc.ui.inbox;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import at.medevit.elexis.ehc.ui.model.EhcDocument;
import at.medevit.elexis.ehc.ui.preference.PreferencePage;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.data.events.ElexisEvent;
import ch.elexis.core.data.events.ElexisEventDispatcher;
import ch.elexis.core.ui.events.ElexisUiEventListenerImpl;
import ch.elexis.data.Mandant;
public class InboxWatcher {
private static Logger logger = LoggerFactory.getLogger(InboxWatcher.class);
private MandantChangedListener mandantListener;
private ExecutorService executor;
private WatchService watcher;
private HashMap<String, WatchKey> watchKeys;
private String activeInboxString;
private List<InboxListener> listeners;
public InboxWatcher(){
executor = Executors.newFixedThreadPool(2);
mandantListener = new MandantChangedListener();
watchKeys = new HashMap<String, WatchKey>();
listeners = new ArrayList<InboxListener>();
ElexisEventDispatcher.getInstance().addListeners(mandantListener);
}
public void start(){
try {
watcher = FileSystems.getDefault().newWatchService();
executor.execute(new DirectoryWatcher());
} catch (IOException e) {
logger.error("Error creating filesystem watcher", e);
}
}
public void stop(){
ElexisEventDispatcher.getInstance().removeListeners(mandantListener);
try {
executor.shutdown();
watcher.close();
} catch (IOException e) {
logger.error("Error closing filesystem watcher", e);
}
}
public synchronized void addInboxListener(InboxListener listener){
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public synchronized void removeInboxListener(InboxListener listener){
listeners.remove(listener);
}
private void fireInboxCreated(EhcDocument newDocument){
for (InboxListener inboxListener : listeners) {
inboxListener.documentCreated(newDocument);
}
}
private class DirectoryDocumentStrategy {
public void execute(URL fileUrl){
if (!EhcDocument.documentExists(fileUrl)) {
if (EhcDocument.isEhcXml(fileUrl)) {
fireInboxCreated(EhcDocument.createFromXml(fileUrl));
} else if (EhcDocument.isEhcXdm(fileUrl)) {
// no inbox element for the XDM
EhcDocument.createFromXdm(fileUrl);
}
}
}
}
private class DirectoryWatcher implements Runnable {
private DirectoryDocumentStrategy documentStrategy;
public DirectoryWatcher(){
documentStrategy = new DirectoryDocumentStrategy();
}
@SuppressWarnings("unchecked")
@Override
public void run(){
WatchKey key = null;
try {
while (true) {
// wait for key to be signaled
key = watcher.take();
// Dequeueing events
Kind<?> kind = null;
for (WatchEvent<?> watchEvent : key.pollEvents()) {
// Get the type of the event
kind = watchEvent.kind();
if (OVERFLOW == kind) {
continue; // loop
} else if (ENTRY_CREATE == kind) {
// A new Path was created
Path newPath = ((WatchEvent<Path>) watchEvent).context();
String newInboxPath = activeInboxString + File.separator
+ newPath.getFileName().toString();
URL fileUrl = new URL("file:///" + newInboxPath);
documentStrategy.execute(fileUrl);
}
}
if (!key.reset()) {
break; // loop
}
}
} catch (InterruptedException | MalformedURLException e) {
logger.error("Filesystem watching interrupted stopping", e);
}
}
}
private class DirectoryInitializer implements Runnable {
private DirectoryDocumentStrategy documentStrategy;
public DirectoryInitializer(){
documentStrategy = new DirectoryDocumentStrategy();
}
@Override
public void run(){
File inboxFolder = new File(activeInboxString);
if (!inboxFolder.exists()) {
PreferencePage.initDirectories();
}
File[] files = inboxFolder.listFiles();
if (files != null) {
for (File file : files) {
if (!file.isDirectory()) {
try {
URL fileUrl = new URL("file:///" + file.getAbsolutePath());
documentStrategy.execute(fileUrl);
} catch (MalformedURLException e) {
logger.error("Error initializing inbox.", e);
}
}
}
}
}
}
private class MandantChangedListener extends ElexisUiEventListenerImpl {
public MandantChangedListener(){
super(Mandant.class, ElexisEvent.EVENT_MANDATOR_CHANGED);
}
@Override
public void runInUi(ElexisEvent ev){
activeInboxString = CoreHub.userCfg.get(PreferencePage.EHC_INPUTDIR,
PreferencePage.getDefaultInputDir());
executor.execute(new DirectoryInitializer());
if (watchKeys.get(activeInboxString) == null) {
try {
Path inboxPath = Paths.get(activeInboxString);
if (!inboxPath.toFile().exists()) {
inboxPath.toFile().mkdirs();
}
WatchKey key;
key = inboxPath.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE);
watchKeys.put(activeInboxString, key);
} catch (IOException e) {
logger.error("Error creating filesystem key", e);
}
}
}
}
}