org.jwebsocket.plugins.filesystem.FileSystemPlugIn.java Source code

Java tutorial

Introduction

Here is the source code for org.jwebsocket.plugins.filesystem.FileSystemPlugIn.java

Source

//   ---------------------------------------------------------------------------
//   jWebSocket - jWebSocket Filesystem Plug-In
//   Copyright (c) 2010 Alexander Schulze, Innotrade GmbH
//   ---------------------------------------------------------------------------
//  THIS CODE IS FOR RESEARCH, EVALUATION AND TEST PURPOSES ONLY!
//  THIS CODE MAY BE SUBJECT TO CHANGES WITHOUT ANY NOTIFICATION!
//   THIS CODE IS NOT YET SECURE AND MAY NOT BE USED FOR PRODUCTION ENVIRONMENTS!
//   ---------------------------------------------------------------------------
//   This program is free software; you can redistribute it and/or modify it
//   under the terms of the GNU Lesser General Public License as published by the
//   Free Software Foundation; either version 3 of the License, or (at your
//   option) any later version.
//   This program is distributed in the hope that it will be useful, but WITHOUT
//   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
//   more details.
//   You should have received a copy of the GNU Lesser General Public License along
//   with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
//   ---------------------------------------------------------------------------
package org.jwebsocket.plugins.filesystem;

import com.google.common.io.ByteStreams;
import com.google.common.io.OutputSupplier;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import javolution.util.FastList;
import javolution.util.FastMap;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.log4j.Logger;
import org.jwebsocket.api.PluginConfiguration;
import org.jwebsocket.api.WebSocketConnector;
import org.jwebsocket.api.WebSocketEngine;
import org.jwebsocket.config.JWebSocketCommonConstants;
import org.jwebsocket.config.JWebSocketConfig;
import org.jwebsocket.config.JWebSocketServerConstants;
import org.jwebsocket.kit.PlugInResponse;
import org.jwebsocket.logging.Logging;
import org.jwebsocket.plugins.TokenPlugIn;
import org.jwebsocket.security.SecurityFactory;
import org.jwebsocket.server.TokenServer;
import org.jwebsocket.token.BaseToken;
import org.jwebsocket.token.Token;
import org.jwebsocket.token.TokenFactory;
import org.jwebsocket.util.Tools;
import org.springframework.context.ApplicationContext;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.web.URLUtils;
import org.jwebsocket.factory.ClassPathUpdater;
import org.jwebsocket.factory.JWebSocketJarClassLoader;
import org.jwebsocket.factory.JWebSocketXmlConfigInitializer;

/**
 *
 * @author aschulze
 */
public class FileSystemPlugIn extends TokenPlugIn {

    private static Logger mLog = Logging.getLogger();
    // if namespace changed update client plug-in accordingly!
    private static final String NS_FILESYSTEM = JWebSocketServerConstants.NS_BASE + ".plugins.filesystem";
    // TODO: make these settings configurable
    private static String PRIVATE_DIR_KEY = "alias:privateDir";
    private static String PUBLIC_DIR_KEY = "alias:publicDir";
    private static String WEB_ROOT_KEY = "alias:webRoot";
    private static String PRIVATE_DIR_DEF = "${" + JWebSocketServerConstants.JWEBSOCKET_HOME
            + "}/private/{username}/";
    private static String PUBLIC_DIR_DEF = "${" + JWebSocketServerConstants.JWEBSOCKET_HOME + "}/public/";
    private static String WEB_ROOT_DEF = "http://jwebsocket.org/";
    private static FileAlterationMonitor mPublicMonitor = null;

    private static ApplicationContext mBeanFactory;
    private static Settings mSettings;
    private OutputSupplier<? extends OutputStream> os;

    /**
     * 
     * @param aConfiguration
     */
    public FileSystemPlugIn(PluginConfiguration aConfiguration) {
        super(aConfiguration);

        if (mLog.isDebugEnabled()) {
            mLog.debug("Instantiating FileSystem plug-in...");
        }
        // specify default name space for admin plugin
        this.setNamespace(NS_FILESYSTEM);

        try {
            mBeanFactory = getConfigBeanFactory();
            if (null == mBeanFactory) {
                mLog.error(
                        "No or invalid spring configuration for filesystem plug-in, some features may not be available.");
            } else {
                mBeanFactory = getConfigBeanFactory();
                mSettings = (Settings) mBeanFactory.getBean("settings");
                if (mLog.isInfoEnabled()) {
                    mLog.info("Filesystem plug-in successfully instantiated.");
                }
            }
        } catch (Exception lEx) {
            mLog.error(Logging.getSimpleExceptionMessage(lEx, "instantiating filesystem plug-in"));
        }
    }

    @Override
    public void engineStarted(WebSocketEngine aEngine) {
        startPublicMonitor(1000);
    }

    @Override
    public void engineStopped(WebSocketEngine aEngine) {
        stopPublicMonitor();
    }

    @Override
    public void processToken(PlugInResponse aResponse, WebSocketConnector aConnector, Token aToken) {
        String lType = aToken.getType();
        String lNS = aToken.getNS();

        if (lType != null && getNamespace().equals(lNS)) {
            // select from database
            mLog.info("[action type] " + lType);
            if (lType.equals("save")) {
                save(aConnector, aToken);
            } else if (lType.equals("saveToHDFS")) {
                try {
                    saveToHDFS(aConnector, aToken);
                } catch (IOException ex) {
                    java.util.logging.Logger.getLogger(FileSystemPlugIn.class.getName()).log(Level.SEVERE, null,
                            ex);
                } catch (InterruptedException ex) {
                    java.util.logging.Logger.getLogger(FileSystemPlugIn.class.getName()).log(Level.SEVERE, null,
                            ex);
                }
            } else if (lType.equals("load")) {
                load(aConnector, aToken);
            } else if (lType.equals("send")) {
                send(aConnector, aToken);
            } else if (lType.equals("getFilelist")) {
                getFilelist(aConnector, aToken);
            } else if (lType.equals("watch")) {
                watch(aConnector, aToken);
            }
        }
    }

    @Override
    public Token invoke(WebSocketConnector aConnector, Token aToken) {
        String lType = aToken.getType();
        String lNS = aToken.getNS();

        if (lType != null && getNamespace().equals(lNS)) {
            if (lType.equals("getFilelist")) {
                return getFilelist(aToken);
            }
        }
        return null;
    }

    /**
     * save a file
     * @param aConnector
     * @param aToken
     */
    private void save(WebSocketConnector aConnector, Token aToken) {
        TokenServer lServer = getServer();
        String lMsg;
        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'save'...");
        }
        // check if user is allowed to run 'save' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".save")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }
        // instantiate response token
        Token lResponse = lServer.createResponse(aToken);

        // obtain required parameters for file load operation
        String lFilename = aToken.getString("filename");
        String lScope = aToken.getString("scope", JWebSocketCommonConstants.SCOPE_PRIVATE);

        // scope may be "private" or "public"
        String lBaseDir;
        if (JWebSocketCommonConstants.SCOPE_PRIVATE.equals(lScope)) {
            String lUsername = getUsername(aConnector);
            lBaseDir = getString(PRIVATE_DIR_KEY, PRIVATE_DIR_DEF);
            if (lUsername != null) {
                lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(lBaseDir).replace("{username}", lUsername);
            } else {
                lMsg = "not authenticated to save private file";
                if (mLog.isDebugEnabled()) {
                    mLog.debug(lMsg);
                }
                lResponse.setInteger("code", -1);
                lResponse.setString("msg", lMsg);
                // send error response to requester
                lServer.sendToken(aConnector, lResponse);
                return;
            }
        } else if (JWebSocketCommonConstants.SCOPE_PUBLIC.equals(lScope)) {
            lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(getString(PUBLIC_DIR_KEY, PUBLIC_DIR_DEF));
        } else {
            lMsg = "invalid scope";
            if (mLog.isDebugEnabled()) {
                mLog.debug(lMsg);
            }
            lResponse.setInteger("code", -1);
            lResponse.setString("msg", lMsg);
            // send error response to requester
            lServer.sendToken(aConnector, lResponse);
            return;
        }

        Boolean lNotify = aToken.getBoolean("notify", false);
        String lData = aToken.getString("data");
        String lEncoding = aToken.getString("encoding", "base64");
        byte[] lBA = null;
        try {
            if ("base64".equals(lEncoding)) {
                int lIdx = lData.indexOf(',');
                if (lIdx >= 0) {
                    lData = lData.substring(lIdx + 1);
                }
                lBA = Base64.decodeBase64(lData);
            } else {
                lBA = lData.getBytes("UTF-8");
            }
        } catch (Exception lEx) {
            mLog.error(Logging.getSimpleExceptionMessage(lEx, "saving file"));
        }

        // complete the response token
        String lFullPath = lBaseDir + lFilename;
        File lFile = new File(lFullPath);
        try {
            // prevent two threads at a time writing to the same file
            synchronized (this) {
                // force create folder if not yet exists
                File lDir = new File(FilenameUtils.getFullPath(lFullPath));
                mLog.debug("[try to create file path] " + lDir.toString());
                FileUtils.forceMkdir(lDir);
                if (lBA != null) {
                    FileUtils.writeByteArrayToFile(lFile, lBA);
                } else {
                    FileUtils.writeStringToFile(lFile, lData, "UTF-8");
                }
            }
        } catch (Exception lEx) {
            lResponse.setInteger("code", -1);
            lMsg = lEx.getClass().getSimpleName() + " on save: " + lEx.getMessage();
            lResponse.setString("msg", lMsg);
            mLog.error(lMsg);
        }

        // send response to requester
        lServer.sendToken(aConnector, lResponse);

        // send notification event to other affected clients
        // to allow to update their content (if desired)
        if (lNotify) {
            // create token of type "event"
            Token lEvent = TokenFactory.createToken(BaseToken.TT_EVENT);
            // include name space of this plug-in
            lEvent.setNS(NS_FILESYSTEM);
            lEvent.setString("name", "filesaved");
            lEvent.setString("filename", lFilename);
            lEvent.setString("sourceId", aConnector.getId());
            lEvent.setString("url", getString(WEB_ROOT_KEY, WEB_ROOT_DEF) + lFilename);
            // TODO: Limit notification to desired scope
            lServer.broadcastToken(lEvent);
        }
    }

    private void saveToHDFS(WebSocketConnector aConnector, Token aToken) throws IOException, InterruptedException {
        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'save to HDFS'...");
        }

        Configuration conf = new Configuration();
        conf.setClassLoader(JWebSocketXmlConfigInitializer.getClassLoader());
        conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");

        FileSystem fs = FileSystem.get(URI.create("hdfs://211.189.127.45:8020"), conf, "yarn");
        mLog.debug("setup HDFS complete.");

        TokenServer lServer = getServer();
        String lMsg;

        // check if user is allowed to run 'save' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".save")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }
        // instantiate response token
        Token lResponse = lServer.createResponse(aToken);

        // obtain required parameters for file load operation
        String lFilename = aToken.getString("filename");
        String lScope = aToken.getString("scope", JWebSocketCommonConstants.SCOPE_PRIVATE);

        // scope may be "private" or "public"
        String lBaseDir;
        if (JWebSocketCommonConstants.SCOPE_PRIVATE.equals(lScope)) {
            String lUsername = getUsername(aConnector);
            lBaseDir = getString(PRIVATE_DIR_KEY, PRIVATE_DIR_DEF);
            if (lUsername != null) {
                lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(lBaseDir).replace("{username}", lUsername);
            } else {
                lMsg = "not authenticated to save private file";
                if (mLog.isDebugEnabled()) {
                    mLog.debug(lMsg);
                }
                lResponse.setInteger("code", -1);
                lResponse.setString("msg", lMsg);
                // send error response to requester
                lServer.sendToken(aConnector, lResponse);
                return;
            }
        } else if (JWebSocketCommonConstants.SCOPE_PUBLIC.equals(lScope)) {
            lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(getString(PUBLIC_DIR_KEY, PUBLIC_DIR_DEF));
        } else {
            lMsg = "invalid scope";
            if (mLog.isDebugEnabled()) {
                mLog.debug(lMsg);
            }
            lResponse.setInteger("code", -1);
            lResponse.setString("msg", lMsg);
            // send error response to requester
            lServer.sendToken(aConnector, lResponse);
            return;
        }

        Boolean lNotify = aToken.getBoolean("notify", false);
        String lData = aToken.getString("data");
        String lEncoding = aToken.getString("encoding", "base64");
        byte[] lBA = null;
        try {
            if ("base64".equals(lEncoding)) {
                int lIdx = lData.indexOf(',');
                if (lIdx >= 0) {
                    lData = lData.substring(lIdx + 1);
                }
                lBA = Base64.decodeBase64(lData);
            } else {
                lBA = lData.getBytes("UTF-8");
            }
        } catch (Exception lEx) {
            mLog.error(Logging.getSimpleExceptionMessage(lEx, "saving file"));
        }

        // complete the response token
        String lFullPath = lBaseDir + lFilename;
        File lFile = new File(lFullPath);
        try {
            // prevent two threads at a time writing to the same file
            synchronized (this) {
                // force create folder if not yet exists
                File lDir = new File(FilenameUtils.getFullPath(lFullPath));
                FileUtils.forceMkdir(lDir);
                if (lBA != null) {
                    FileUtils.writeByteArrayToFile(lFile, lBA);

                    OutputSupplier<? extends OutputStream> os = (OutputSupplier<? extends OutputStream>) fs
                            .create(new Path(fs.getHomeDirectory(), lFilename), true);
                    ByteStreams.write(lBA, os);
                } else {
                    FileUtils.writeStringToFile(lFile, lData, "UTF-8");
                }
            }
        } catch (Exception lEx) {
            lResponse.setInteger("code", -1);
            lMsg = lEx.getClass().getSimpleName() + " on save: " + lEx.getMessage();
            lResponse.setString("msg", lMsg);
            mLog.error(lMsg);
        }

        // send response to requester
        lServer.sendToken(aConnector, lResponse);

        // send notification event to other affected clients
        // to allow to update their content (if desired)
        if (lNotify) {
            // create token of type "event"
            Token lEvent = TokenFactory.createToken(BaseToken.TT_EVENT);
            // include name space of this plug-in
            lEvent.setNS(NS_FILESYSTEM);
            lEvent.setString("name", "filesaved");
            lEvent.setString("filename", lFilename);
            lEvent.setString("sourceId", aConnector.getId());
            lEvent.setString("url", getString(WEB_ROOT_KEY, WEB_ROOT_DEF) + lFilename);
            // TODO: Limit notification to desired scope
            lServer.broadcastToken(lEvent);
        }
    }

    /**
     * load a file
     * @param aConnector
     * @param aToken
     */
    private void load(WebSocketConnector aConnector, Token aToken) {
        TokenServer lServer = getServer();
        String lMsg;

        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'load'......");
        }

        // check if user is allowed to run 'load' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".load")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }

        // obtain required parameters for file load operation
        String lFilename = aToken.getString("filename");
        String lScope = aToken.getString("scope", JWebSocketCommonConstants.SCOPE_PRIVATE);
        String lData = "";

        // instantiate response token
        Token lResponse = lServer.createResponse(aToken);

        String lBaseDir;
        if (JWebSocketCommonConstants.SCOPE_PRIVATE.equals(lScope)) {
            String lUsername = getUsername(aConnector);
            lBaseDir = getString(PRIVATE_DIR_KEY, PRIVATE_DIR_DEF);
            if (lUsername != null) {
                lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(lBaseDir).replace("{username}", lUsername);
            } else {
                lMsg = "not authenticated to load private file";
                if (mLog.isDebugEnabled()) {
                    mLog.debug(lMsg);
                }
                lResponse.setInteger("code", -1);
                lResponse.setString("msg", lMsg);
                // send error response to requester
                lServer.sendToken(aConnector, lResponse);
                return;
            }
        } else if (JWebSocketCommonConstants.SCOPE_PUBLIC.equals(lScope)) {
            lBaseDir = JWebSocketConfig.expandEnvAndJWebSocketVars(getString(PUBLIC_DIR_KEY, PUBLIC_DIR_DEF));
        } else {
            lMsg = "invalid scope";
            if (mLog.isDebugEnabled()) {
                mLog.debug(lMsg);
            }
            lResponse.setInteger("code", -1);
            lResponse.setString("msg", lMsg);
            // send error response to requester
            lServer.sendToken(aConnector, lResponse);
            return;
        }

        // complete the response token
        File lFile = new File(lBaseDir + lFilename);
        byte[] lBA = null;
        try {
            lBA = FileUtils.readFileToByteArray(lFile);
            if (lBA != null && lBA.length > 0) {
                lData = new String(Base64.encodeBase64(lBA), "UTF-8");
            }
            lResponse.setString("data", lData);
        } catch (Exception lEx) {
            lResponse.setInteger("code", -1);
            lMsg = lEx.getClass().getSimpleName() + " on load: " + lEx.getMessage();
            lResponse.setString("msg", lMsg);
            mLog.error(lMsg);
        }

        // send response to requester
        lServer.sendToken(aConnector, lResponse);
    }

    /**
     * send a file from one client to another client
     * @param aConnector
     * @param aToken
     */
    private void send(WebSocketConnector aConnector, Token aToken) {
        TokenServer lServer = getServer();
        String lMsg;

        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'send'...");
        }

        // check if user is allowed to run 'save' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".send")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }

        String lFilename = aToken.getString("filename");
        String lData = aToken.getString("data");
        String lNodeId = aToken.getString("unid");
        String lTargetId = aToken.getString("targetId");

        // instantiate response token
        Token lResponse = lServer.createResponse(aToken);

        WebSocketConnector lTarget = null;
        if (lNodeId != null) {
            lTarget = lServer.getNode(lNodeId);
        } else if (lTargetId != null) {
            lTarget = lServer.getConnector(lTargetId);
        }
        if (lTarget != null) {
            // send notification event to target client
            // to allow to update their content (if desired)
            // create token of type "event"
            Token lEvent = TokenFactory.createToken(BaseToken.TT_EVENT);
            // include name space of this plug-in
            lEvent.setNS(NS_FILESYSTEM);
            lEvent.setString("name", "filesent");
            lEvent.setString("filename", lFilename);
            lEvent.setString("sourceId", aConnector.getId());
            lEvent.setString("data", lData);
            // send file to target client
            lServer.sendToken(lTarget, lEvent);
        } else {
            lMsg = "target '" + lTargetId + "' not found";
            if (mLog.isDebugEnabled()) {
                mLog.debug(lMsg);
            }
            lResponse.setInteger("code", -1);
            lResponse.setString("msg", lMsg);
        }

        // send response to requester
        lServer.sendToken(aConnector, lResponse);
    }

    private Token getFilelist(Token aToken) {

        String lAlias = aToken.getString("alias");
        boolean lRecursive = aToken.getBoolean("recursive", false);
        List lFilemasks = aToken.getList("filemasks");

        Object lObject = null;
        String lFolder = null;
        Token lToken = TokenFactory.createToken();

        if (mSettings != null) {
            lObject = mSettings.getAlias(lAlias);
            if (lObject != null) {
                lFolder = (String) lObject;
                File lDir = new File(JWebSocketConfig.expandEnvAndJWebSocketVars(lFolder));
                lFolder = lDir.getPath();
                // IOFileFilter lFileFilter = FileFilterUtils.nameFileFilter(lFilemask);
                String[] lFilemaskArray = new String[lFilemasks.size()];
                int lIdx = 0;
                for (Object lMask : lFilemasks) {
                    lFilemaskArray[lIdx] = (String) lMask;
                    lIdx++;
                }
                IOFileFilter lFileFilter = new WildcardFileFilter(lFilemaskArray);
                IOFileFilter lDirFilter = null;
                if (lRecursive) {
                    lDirFilter = FileFilterUtils.directoryFileFilter();
                }
                Collection<File> lFiles = FileUtils.listFiles(lDir, lFileFilter, lDirFilter);
                List lFileList = new FastList<Map>();
                for (File lFile : lFiles) {
                    Map lFileData = new FastMap<String, Object>();
                    String lName = lFile.getName();
                    lFileData.put("filename", lName);
                    String lPath = lFile.getPath();
                    if (lPath != null && lPath.indexOf(lFolder) == 0) {
                        lPath = lPath.substring(lFolder.length() + 1, lPath.length() - lName.length());
                    }
                    lFileData.put("path", lPath);
                    lFileData.put("size", lFile.length());
                    lFileData.put("modified", Tools.DateToISO8601(new Date(lFile.lastModified())));
                    lFileData.put("hidden", lFile.isHidden());
                    lFileData.put("canRead", lFile.canRead());
                    lFileData.put("canWrite", lFile.canWrite());
                    lFileList.add(lFileData);
                }
                lToken.setList("files", lFileList);
                lToken.setInteger("code", 0);
                lToken.setString("msg", "ok");
            } else {
                lToken.setInteger("code", -1);
                lToken.setString("msg", "no alias '" + lAlias + "' defined for filesystem plug-in");
            }
        } else {
            lToken.setInteger("code", -1);
            lToken.setString("msg", "no settings defined for filesystem plug-in");
        }
        return lToken;
    }

    private void getFilelist(WebSocketConnector aConnector, Token aToken) {
        TokenServer lServer = getServer();

        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'getFilelist'...");
        }

        // check if user is allowed to run 'save' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".getFilelist")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }

        Token lResponse = getFilelist(aToken);
        lServer.setResponseFields(aToken, lResponse);

        // send response to requester
        lServer.sendToken(aConnector, lResponse);
    }

    class ChangeListener implements FileAlterationListener {

        // Directory changed Event.
        @Override
        public void onDirectoryChange(File aDirectory) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Directory " + aDirectory.getName() + " has been changed.");
            }
        }

        // Directory created Event.
        @Override
        public void onDirectoryCreate(File aDirectory) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Directory " + aDirectory.getName() + " has been created.");
            }
        }

        //  Directory deleted Event.
        @Override
        public void onDirectoryDelete(File aDirectory) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Directory " + aDirectory.getName() + " has been deleted.");
            }
        }

        // File changed Event.
        @Override
        public void onFileChange(File aFile) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("File " + aFile.getName() + " has been changed...!!");
            }
        }

        // File created Event.
        @Override
        public void onFileCreate(File aFile) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("File " + aFile.getName() + " has been created.");
            }
        }

        // File deleted Event.
        @Override
        public void onFileDelete(File aFile) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("File " + aFile.getName() + " has been deleted.");
            }
        }

        // File system observer started checking event.
        @Override
        public void onStart(FileAlterationObserver aObserver) {
            /*
            if (mLog.isDebugEnabled()) {
            mLog.debug("Monitor has been started.");
            }
             */
        }

        // File system observer finished checking event.      
        @Override
        public void onStop(FileAlterationObserver aObserver) {
            /*
            if (mLog.isDebugEnabled()) {
            mLog.debug("Monitor has been stopped.");
            }
             */
        }
    }

    // inner helper class to just to give the 
    // monitor thread a name for maintainability
    class MonitorThreadFactory implements ThreadFactory {

        @Override
        public Thread newThread(Runnable aRunnable) {
            Thread lThread = new Thread(aRunnable, "jWebSocket FileSystemPlugIn Monitor");
            return lThread;
        }
    }

    /**
     * 
     * @param aInterval
     */
    public void startPublicMonitor(int aInterval) {
        if (null == mPublicMonitor) {
            mPublicMonitor = new FileAlterationMonitor(aInterval);
            mPublicMonitor.setThreadFactory(new MonitorThreadFactory());
            String lBaseDir = JWebSocketConfig
                    .expandEnvAndJWebSocketVars(getString(PUBLIC_DIR_KEY, PUBLIC_DIR_DEF));
            String lMask = "*";
            IOFileFilter lFileFilter = new WildcardFileFilter(lMask);
            if (mLog.isDebugEnabled()) {
                mLog.debug("Starting public monitor for " + lBaseDir + ", files:" + lMask + "...");
            }
            FileAlterationObserver lObserver = new FileAlterationObserver(lBaseDir, lFileFilter);
            FileAlterationListener lListener = new ChangeListener();
            lObserver.addListener(lListener);
            mPublicMonitor.addObserver(lObserver);
        }
        try {
            mPublicMonitor.start();
        } catch (Exception lEx) {
            mLog.error(Logging.getSimpleExceptionMessage(lEx, "starting monitor"));
        }
    }

    /**
     * 
     */
    public void stopPublicMonitor() {
        if (null != mPublicMonitor) {
            try {
                if (mLog.isDebugEnabled()) {
                    mLog.debug("Stopping public monitor...");
                }
                mPublicMonitor.stop();
            } catch (Exception lEx) {
                mLog.error(Logging.getSimpleExceptionMessage(lEx, "stopping monitor"));
            }
        }
    }

    private void watch(WebSocketConnector aConnector, Token aToken) {
        TokenServer lServer = getServer();

        if (mLog.isDebugEnabled()) {
            mLog.debug("Processing 'watch'...");
        }

        // check if user is allowed to run 'save' command
        if (!SecurityFactory.hasRight(lServer.getUsername(aConnector), NS_FILESYSTEM + ".watch")) {
            if (mLog.isDebugEnabled()) {
                mLog.debug("Returning 'Access denied'...");
            }
            lServer.sendToken(aConnector, lServer.createAccessDenied(aToken));
            return;
        }

        String lPath = aToken.getString("path");
        String lFilename = aToken.getString("filename");

        Token lResponse = createResponse(aToken);

        // send response to requester
        lServer.sendToken(aConnector, lResponse);
    }

}