coral.service.ExpServable.java Source code

Java tutorial

Introduction

Here is the source code for coral.service.ExpServable.java

Source

/*
 *   Copyright 2009-2015 Markus Schaffner
 *
 *   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 coral.service;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

import javax.activation.MimetypesFileTypeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import any.Linker;
import any.model.Message;
import any.model.Servable;
import any.servable.GetServable;
import coral.data.DataService;
import coral.data.DataServiceJbdcImpl;
import coral.utils.CoralUtils;
import coral.utils.WaitForFileThread;

public class ExpServable implements Servable, ExpHandler {

    protected final static Log logger = LogFactory.getLog(ExpServable.class);

    // Map with all the clients queues
    private Map<Integer, BlockingQueue<Message>> clients = new HashMap<Integer, BlockingQueue<Message>>();

    private IExpService service = null; // Service
    private DataService dataService;

    private ExpServer server;

    private Map<Integer, FileWriter> screenwriter = new HashMap<Integer, FileWriter>();
    private boolean useScreenwriter = false;

    private Integer clientCount = 0;

    private String basepath = "";
    private String stagesfile = "stages.csv";
    private String viewname;

    // SERVER INFO
    // Thread serverthread = null;
    // BlockingQueue<Message> serverqueue = null;

    private String startMarker;
    private String refreshMarker;
    private String serverMarker;

    Linker linker = null;

    String servervm = "info.vm";

    public ExpServable() {
    }

    public void process(Message cmd, BlockingQueue<Message> outQueue) {
        Integer id = getClientId(outQueue);
        String c = cmd.getFullContent();

        // remove leading slash /
        c.replaceAll("^/", "");

        logger.info("run cmd on server: ####" + c + "#### to " + id);

        if (c.startsWith("__RES")) {
            logger.info("send resources");
            sendResources(outQueue);
        } else if (c.startsWith("__FILE/")) {
            logger.info("client " + id + " requests file " + c);
            int pos = c.indexOf("__FILE/");
            String filename = c.substring(pos + 7);
            service.evalTemplate(id, filename);
        } else if (c.startsWith(serverMarker)) {
            logger.info("client " + id + " requests server " + c);
            server.process(id, c, outQueue);
        } else if (c.startsWith(refreshMarker)) {
            id = Integer.parseInt(c.replaceAll("[^\\d]", ""));
            logger.info("refresh client " + id + " " + c);

            BlockingQueue<Message> oldq = clients.get(id);
            if (oldq != null) {
                clients.remove(oldq);
                clients.put(id, outQueue);

                service.process(id, "?refreshid=" + id);
            } else {
                logger.info("START new client with this id");
                clients.put(id, outQueue);
                service.addClient(id);
                if (useScreenwriter) {
                    try {
                        FileWriter fw = new FileWriter(new File("client_screens" + id + ".html"));
                        screenwriter.put(id, fw);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                service.process(id, "?refreshid=" + id);
            }
        } else if (id == null) {
            logger.info("START new client with no id");
            clientCount = dataService.getNewId(clientCount + 1);
            clients.put(clientCount, outQueue);
            service.addClient(clientCount);
            id = clientCount;
            if (useScreenwriter) {
                try {
                    FileWriter fw = new FileWriter(new File("client_screens" + id + ".html"));
                    screenwriter.put(id, fw);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            service.process(id, c);
        } else if (c.startsWith(startMarker)) {
            logger.info("START new client (discard old one)");
            clients.remove(outQueue);
            clientCount = dataService.getNewId(clientCount + 1);
            clients.put(clientCount, outQueue);
            service.addClient(clientCount);
            id = clientCount;
            service.process(id, c);
        } else if (c.startsWith(CoralUtils.KILL_KEY)) {

            // kills the client with the given id (any number in the command)
            id = Integer.parseInt(cmd.getQuery().get("id"));
            logger.info("KILL client " + id + " " + c);

            BlockingQueue<Message> oldq = clients.get(id);
            if (oldq != null) {
                oldq.add(new Message("vset:_error?content=client has been removed", new byte[] {}));
                clients.remove(oldq);
                service.removeClient(id);
                outQueue.add(new Message("vset:_error?content=client " + id + " has been removed "));
            } else {
                logger.info("client id " + id + " did not exist");
                outQueue.add(new Message("vset:_error?content=client " + id + " does not exist "));
            }
            // clients.remove(outQueue);
            logger.info("client  discarded");
        } else {
            service.process(id, cmd.getFullContent());
        }

    }

    @Deprecated
    private void sendResources(BlockingQueue<Message> outQueue) {
        BufferedReader br;
        try {
            br = new BufferedReader(new FileReader(new File(basepath + "resources.csv")));

            String line = br.readLine();
            logger.info("start add resources: " + line);
            while ((line = br.readLine()) != null) {
                sendRes(outQueue, null, line);
            }
        } catch (FileNotFoundException e) {
            logger.error("file not found", e);
        } catch (IOException e) {
            logger.error("I/O", e);
        }
    }

    /*
     * sends a resourceto id
     */
    @Override
    public void sendRes(Integer id, String... res) {
        BlockingQueue<Message> outQueue = clients.get(id);
        sendRes(outQueue, res);

    }

    /*
     * sends a resource to queue (internal)
     */
    private void sendRes(BlockingQueue<Message> outQueue, String... res) {
        for (String line : res) {
            try {
                File file = new File(basepath, line);

                long version = file.lastModified();

                logger.info("sync file " + line + " version:" + version + " path " + file.getCanonicalPath());

                try {
                    /*
                     * outQueue.put(new Message("ressync:" + file.getName() +
                     * "?show=NO&version=" + version + "&synccmd=" +
                     * URLEncoder.encode("get://host/" +
                     * file.getCanonicalPath(), "utf-8"), new byte[] {}));
                     */

                    // InputStream is;
                    // byte[] bytes = null;

                    String mime = GetServable.getMime(file);

                    BufferedInputStream reader = new BufferedInputStream(new FileInputStream(file));

                    byte[] buffer = new byte[8096]; // == 1024

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    int read;
                    while ((read = reader.read(buffer)) >= 0) {
                        baos.write(buffer, 0, read);
                    }
                    byte[] readbytes = baos.toByteArray();

                    outQueue.put(new Message("vset", file.getName(), mime, "NO&version=" + version, readbytes));

                    reader.close();

                } catch (InterruptedException e) {
                    logger.error("i error", e);
                }
            } catch (FileNotFoundException e) {
                logger.error("file not found", e);
            } catch (IOException e) {
                logger.error("I/O", e);
            }
        }
    }

    @Override
    public void broadcast(Integer id, String msg) {
        BlockingQueue<Message> outQueue = clients.get(id);

        FileWriter fw = screenwriter.get(id);
        if (fw != null) {
            try {
                fw.append(msg);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        outQueue.add(new Message("vset", viewname, "text/html", "YES", msg.getBytes()));
    }

    public synchronized void wait(Integer id, String mode, int stageid, int loop) {
        if (mode.equals("all")) {
            waitForAll(id, stageid, loop);
        } else if (mode.endsWith(".txt")) {
            waitForFile(id, mode, stageid, loop);
        }
    }

    private Map<Integer, WaitForFileThread> waitForFile;

    private void waitForFile(Integer id, String mode, Integer stageid, Integer loop) {
        File fileWaitFor = new File(mode);

        if (fileWaitFor.exists()) {
            // if the file exists, just proceed
            service.process(id, "/?wait=_waited&_waitOnFile=existed");
        } else {
            /*
             * here it get a bit more involved, create or join a Thread that
             * waits for the file to come into existence and then proceed
             */
            int stage = stageid * 1000 + loop;

            if (waitForFile == null) {
                waitForFile = new HashMap<Integer, WaitForFileThread>();
            }

            synchronized (waitForFile) {

                if (!waitForFile.containsKey(stage) || !waitForFile.get(stage).isAlive()) {
                    logger.debug("wait for file " + mode + " + stage " + stage + " first create thread");
                    WaitForFileThread t = new WaitForFileThread(this, fileWaitFor);
                    waitForFile.put(stage, t);
                    t.start();

                }
                // join thread
                if (waitForFile.get(stage).add(id)) {
                    logger.debug("wait for file " + mode + " + stage " + stage + ": "
                            + " == joined thread by client id: " + id);
                }
            }
        }
    }

    private Map<Integer, Set<Integer>> waitForAll;

    private void waitForAll(Integer id, Integer stageid, Integer loop) {

        int stage = stageid * 1000 + loop;

        if (waitForAll == null) {
            waitForAll = new HashMap<Integer, Set<Integer>>();
        }

        synchronized (waitForAll) {

            if (!waitForAll.containsKey(stage)) {
                waitForAll.put(stage, new HashSet<Integer>());
            }
            if (waitForAll.get(stage).add(id)) {
                logger.debug("wait for all stage " + stage + ": " + waitForAll.get(stage).size() + " ==  "
                        + clients.size());
                if (waitForAll.get(stage).size() == clients.size()) {
                    try {
                        // give it a bit breathing time for loops (e.g. only
                        // one client signed in)
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    for (Integer i : waitForAll.get(stage)) {
                        service.process(i, "?wait=_waited&_waitnr=" + clients.size());

                    }
                    waitForAll.remove(stage);
                }
            }
        }

    }

    @Override
    public IExpService getService() {
        return service;
    }

    @Override
    public Set<Integer> getServiceIds() {
        return ((ExpServiceImpl) service).dataMap.keySet();
    }

    public static String getMime(File file) {

        String mime = "text/plain";
        try {
            mime = new MimetypesFileTypeMap().getContentType(file);
        } catch (Error ex) {
            logger.error("Problem with mime type resolution.", ex);

            String n = file.getName();
            String e = n.substring(n.lastIndexOf('.') + 1).toLowerCase();

            String[][] types = new String[][] { new String[] { "pdf", "application/pdf" },
                    new String[] { "html", "text/html" }, new String[] { "png", "image/png" },
                    new String[] { "anytable", "text/anytable" }, new String[] { "jpg", "image/jpg" } };

            for (String[] r : types) {
                if (r[0].equals(e)) {
                    mime = r[1];
                }
            }
        }

        return mime;
    }

    @Override
    public void init(Properties properties, BlockingQueue<Message> loopQueue, Linker linker) {

        this.linker = linker;

        this.basepath = properties.getProperty("exp.basepath", "./");

        String dbname = properties.getProperty("coral.db.name", "db");
        String dbmode = properties.getProperty("coral.db.mode", "file");
        boolean resetdb = (properties.getProperty("coral.db.reset", "false").equals("true"));
        dataService = new DataServiceJbdcImpl(dbname, resetdb, dbmode);
        CoralUtils.hoststr = properties.getProperty("coral.head.coralhost", "exp://host/");
        ExpServiceImpl serv = new ExpServiceImpl(this, properties, dataService);

        if (properties.containsKey("exp.stagesfile")) {
            this.stagesfile = properties.getProperty("exp.stagesfile");
        }
        serv.init(basepath, stagesfile, properties.getProperty("coral.exp.variant"));

        this.viewname = properties.getProperty("exp.viewname", "_exp.html");

        this.server = new ExpServer(0, properties.getProperty("exp.servertype", "none"), this, dataService);

        serv.debug = (properties.getProperty("coral.debug", "false").equals("true"));
        logger.debug("debug status is " + properties.getProperty("exp.debug", "false") + "  --  " + serv.debug);

        useScreenwriter = (properties.getProperty("coral.head.screenwriter", "false").equals("true"));

        this.startMarker = properties.getProperty("coral.cmd.start", CoralUtils.START_KEY);
        this.refreshMarker = properties.getProperty("coral.cmd.refresh", CoralUtils.REFRESH_KEY);
        this.serverMarker = properties.getProperty("coral.cmd.server", CoralUtils.SERVER_KEY);

        this.service = serv;
    }

    @Override
    public void disconnect(BlockingQueue<Message> outQueue) {
        // default, ignore
    }

    public ArrayList<Map<String, Object>> getClientInfoMapList() {
        ArrayList<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

        for (String name : linker.getNamedCon().keySet()) {
            Map<String, Object> c = new HashMap<String, Object>();

            c.put("name", name);

            BlockingQueue<Message> queue = linker.getNamedCon().get(name).getOutQueue();

            Integer id = getClientId(queue);

            if (id != null) {
                c.put("connected", true);
                c.put("id", id);
                c.put("data", service.getData(id));
            } else {
                c.put("connected", false);
            }

            result.add(c);
        }

        return result;
    }

    public Integer getClientId(BlockingQueue<Message> queue) {
        return CoralUtils.getKeyByValue(clients, queue);
    }

}