org.infoscoop.service.GadgetResourceService.java Source code

Java tutorial

Introduction

Here is the source code for org.infoscoop.service.GadgetResourceService.java

Source

/* infoScoop OpenSource
 * Copyright (C) 2010 Beacon IT Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 * 
 * 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-3.0-standalone.html>.
 */

package org.infoscoop.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.serializer.dom3.LSSerializerImpl;
import org.apache.xpath.XPathAPI;
import org.infoscoop.dao.GadgetDAO;
import org.infoscoop.dao.GadgetIconDAO;
import org.infoscoop.dao.model.Gadget;
import org.infoscoop.util.NoOpEntityResolver;
import org.infoscoop.util.SpringUtil;
import org.infoscoop.util.XmlUtil;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.LSSerializer;

public class GadgetResourceService {
    private static Log log = LogFactory.getLog(GadgetResourceService.class);

    public static Pattern REGEX_NAME = Pattern.compile("^[\\w-.]+");
    public static Pattern REGEX_PATH = Pattern.compile("^(?:/[\\w-.]*)+");

    private GadgetDAO gadgetDAO;
    private GadgetIconDAO gadgetIconDAO;

    public static GadgetResourceService getHandle() {
        return (GadgetResourceService) SpringUtil.getBean("GadgetResourceService");
    }

    public GadgetResourceService() {
    }

    public void setGadgetDAO(GadgetDAO gadgetDAO) {
        this.gadgetDAO = gadgetDAO;
    }

    public void setGadgetIconDAO(GadgetIconDAO gadgetIconDAO) {
        this.gadgetIconDAO = gadgetIconDAO;
    }

    public Gadget getResource(String type, String path, String name) {
        return gadgetDAO.select(type, path, name);
    }

    public byte[] selectResource(String type, String path, String name) {
        Gadget gadget = gadgetDAO.select(type, path, name);
        if (gadget == null)
            return null;

        return gadget.getData();
    }

    public String selectResource(String type, String path, String name, String encoding) {
        try {
            return new String(selectResource(type, path, name), encoding);
        } catch (UnsupportedEncodingException ex) {
            throw new RuntimeException(ex);
        }
    }

    public Collection<JSONObject> getResourceListJson(String type) {
        Collection<JSONObject> resources = new ArrayList<JSONObject>();
        for (Gadget resource : gadgetDAO.list(type)) {
            JSONObject rMap = new JSONObject();

            try {
                rMap.put("path", resource.getPath());
                rMap.put("name", resource.getName());
            } catch (JSONException ex) {
                throw new RuntimeException(ex);
            }

            resources.add(rMap);
        }

        return resources;
    }

    public void deleteResource(String type, String path, String name) {
        List<Gadget> list = gadgetDAO.list(type, path);

        if (gadgetDAO.delete(type, path, name) && list.size() == 1)
            gadgetDAO._insert(type, path, "", new byte[0]);

        log.info("delete gadget resource [" + type + "] " + path + name);
    }

    public void insertResource(String type, String path, String name, byte[] data) throws GadgetResourceException {
        if (!REGEX_NAME.matcher(type).matches())
            throw new GadgetResourceException("invalid type: " + type, "ams_gadgetResourceInvalidType");

        if (!REGEX_NAME.matcher(name).matches())
            throw new GadgetResourceException("invalid name:" + name, "ams_gadgetResourceInvalidName");

        if (!REGEX_PATH.matcher(path).matches())
            throw new GadgetResourceException("invalid path: " + path, "ams_gadgetResourceInvalidPath");

        if (name.getBytes().length > 255)
            throw new GadgetResourceException("too long name,name must be under 255 byte: " + name,
                    "ams_gadgetResourceTooLongName");

        if (path.getBytes().length > 512)
            throw new GadgetResourceException("too long path,path must be under 512 byte: " + path,
                    "ams_gadgetResourceTooLongPath");

        if (gadgetDAO.select(type, path, name) != null)
            throw new GadgetResourceException("already existed resource at [" + path + "/" + name + "]",
                    "ams_gadgetResourceNameAlreadyExisted");

        gadgetDAO.insert(type, path, name, data);

        log.info("insert gadget resource [" + type + "] " + path + name);
    }

    public void updateTextResource(String type, String path, String name, String data) {
        try {
            updateResource(type, path, name, data.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException ex) {
            // ignore
        }
    }

    public void updateResource(String type, String path, String name, byte[] data) throws GadgetResourceException {
        if (name.equals(type + ".xml") && "/".equals(path))
            validateGadgetData(type, path, name, data);

        gadgetDAO.update(type, path, name, data);

        log.info("update gadget resource [" + type + "] " + path + name);
    }

    public void updateResources(String type, InputStream in) throws GadgetResourceException {
        gadgetDAO.deleteType(type);
        //gadgetIconDAO.deleteByType(type);

        byte[] data;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[5120];
            int reads = 0;
            while (!((reads = in.read(buf)) < 0))
                baos.write(buf, 0, reads);

            data = baos.toByteArray();
        } catch (IOException ex) {
            throw new GadgetResourceException(ex);
        }

        data = validateGadgetData(type, "/", type + ".xml", data);

        insertResource(type, "/", type + ".xml", data);
    }

    public void updateResources(String type, String moduleName, ZipInputStream zin) throws GadgetResourceException {
        gadgetDAO.deleteType(type);
        //gadgetIconDAO.deleteByType(type);

        boolean findModule = false;
        try {
            ZipEntry entry;
            while ((entry = getNextEntry(zin)) != null) {
                if (!entry.isDirectory()) {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buf = new byte[5120];
                    int reads = 0;
                    while (!((reads = zin.read(buf)) < 0))
                        baos.write(buf, 0, reads);
                    byte[] data = baos.toByteArray();

                    String zipName = entry.getName();
                    String path = "/" + zipName.substring(0, zipName.lastIndexOf("/") + 1);
                    String name = zipName.substring(path.length() - 1);

                    try {
                        if (path.length() > 1 && path.endsWith("/"))
                            path = path.substring(0, path.length() - 1);

                        if ("/".equals(path) && (moduleName + ".xml").equalsIgnoreCase(name)) {
                            findModule = true;
                            name = type + ".xml";

                            data = validateGadgetData(type, path, name, data);
                        }

                        insertResource(type, path, name, data);
                    } catch (Exception ex) {
                        throw new GadgetResourceArchiveException(path, name,
                                "It is an entry of an invalid archive.  error at [" + path + "/" + name + "]"
                                        + ex.getMessage(),
                                "ams_gadgetResourceInvalidArchiveEntry", ex);
                    }
                }

                zin.closeEntry();
            }
        } catch (GadgetResourceException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("", ex);

            throw new GadgetResourceException("It is an invalid archive.", "ams_gadgetResourceInvalidArchive");
        }

        if (!findModule)
            throw new GadgetResourceException(
                    "Not found gadget module( /" + moduleName + ".xml" + " ) in an uploaded archive.",
                    "ams_gadgetResourceNotFoundGadgetModule");
    }

    private ZipEntry getNextEntry(ZipInputStream zin) throws IOException {
        try {
            return zin.getNextEntry();
        } catch (IllegalArgumentException ex) {
            throw new GadgetResourceException("invalid name", "ams_gadgetResourceInvalidName");
        }
    }

    private byte[] validateGadgetData(String type, String path, String name, byte[] data) {
        Document gadgetDoc;

        try {
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            builderFactory.setValidating(false);
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            builder.setEntityResolver(NoOpEntityResolver.getInstance());
            gadgetDoc = builder.parse(new ByteArrayInputStream(data));

            Element moduleNode = gadgetDoc.getDocumentElement();
            NodeList contentNodes = moduleNode.getElementsByTagName("Content");
            //The preparations for Locale tag
            NodeList modulePrefsList = gadgetDoc.getElementsByTagName("ModulePrefs");

            if (!"Module".equals(moduleNode.getTagName()) || contentNodes == null || contentNodes.getLength() == 0
                    || modulePrefsList == null || modulePrefsList.getLength() == 0) {
                throw new GadgetResourceException("It is an invalid gadget module. ",
                        "ams_gadgetResourceInvalidGadgetModule");
            }

            Element iconElm = (Element) XPathAPI.selectSingleNode(gadgetDoc, "/Module/ModulePrefs/Icon");
            if (iconElm != null) {
                String iconUrl = iconElm.getTextContent();
                for (int i = 0; i < modulePrefsList.getLength(); i++) {
                    Element modulePrefs = (Element) modulePrefsList.item(i);
                    if (modulePrefs.hasAttribute("resource_url")) {
                        iconUrl = modulePrefs.getAttribute("resource_url") + iconUrl;
                        break;
                    }
                }
                gadgetIconDAO.insertUpdate(type, iconUrl);
            } else {
                gadgetIconDAO.insertUpdate(type, "");
            }

            return XmlUtil.dom2String(gadgetDoc).getBytes("UTF-8");
        } catch (GadgetResourceException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new GadgetResourceException("It is an invalid gadget module. ",
                    "ams_gadgetResourceInvalidGadgetModule", ex);
        }
    }

    public byte[] selectResourcesZip(String type) throws GadgetResourceException {
        Map<String, Object> tree = new TreeMap<String, Object>();
        for (Gadget gadget : gadgetDAO.list(type)) {
            Stack<String> path = new Stack<String>();
            List<String> pl = Arrays.asList(gadget.getPath().substring(1).split("/"));
            Collections.reverse(pl);
            path.addAll(pl);

            putResource(tree, path, gadget.getName(), gadget.getData());
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            ZipOutputStream zout = new ZipOutputStream(baos);
            popResource(tree, "", zout);
            zout.close();
        } catch (IOException ex) {
            throw new GadgetResourceException(ex);
        }

        return baos.toByteArray();
    }

    private static void putResource(Map<String, Object> tree, Stack<String> path, String name, byte[] data) {
        if (path.size() > 0) {
            String pathSeg = "#" + path.pop();
            if (!tree.containsKey(pathSeg))
                tree.put(pathSeg, new TreeMap<String, Object>());

            putResource((Map<String, Object>) tree.get(pathSeg), path, name, data);
        } else if (name != null) {
            tree.put(name, data);
        }
    }

    private static void popResource(Map<String, Object> tree, String path, ZipOutputStream zout)
            throws IOException {
        if (path.equals("/"))
            path = "";

        for (String key : tree.keySet()) {
            Object value = tree.get(key);
            if (key.startsWith("#"))
                key = key.substring(1);

            ZipEntry entry = new ZipEntry(path + key);
            if (value instanceof Map) {
                entry.setMethod(ZipEntry.STORED);
                entry.setSize(0);
                entry.setCrc(0);

                //            zout.putNextEntry( entry );
                popResource((Map<String, Object>) value, path + key + "/", zout);
                //            zout.closeEntry();
            } else {
                byte[] data = (byte[]) tree.get(key);
                entry.setSize(data.length);

                zout.putNextEntry(entry);
                zout.write(data);
                zout.closeEntry();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        String longName = "";
        for (int i = 0; i < 510; i++)
            longName += "a";

        ZipOutputStream zout = new ZipOutputStream(new FileOutputStream("test.zip"));
        ZipEntry ze = new ZipEntry(longName + "/a");
        zout.putNextEntry(ze);
        zout.write("test".getBytes());
        zout.closeEntry();
        zout.close();
    }

    public static class GadgetResourceException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public GadgetResourceException() {
            this(null);
        }

        public GadgetResourceException(Throwable cause) {
            this("unexcepted error", "ams_gadgetResourceUnknownError", cause);
        }

        public GadgetResourceException(String message, String id) {
            this(message, id, null);
        }

        public GadgetResourceException(String message, String id, Throwable cause) {
            super(message, cause);

            this.id = id;
        }

        private String id;

        public String getId() {
            return id;
        }

        public JSONObject toJSON() {
            JSONObject json = new JSONObject();
            try {
                json.put("message", getId());
            } catch (JSONException ex) {
                // ignore
            }

            return json;
        }
    }

    public static class GadgetResourceArchiveException extends GadgetResourceException {
        private static final long serialVersionUID = 1L;

        private String path;
        private String name;

        public GadgetResourceArchiveException(String path, String name, String message, String id,
                Throwable cause) {
            super(message, id, cause);

            this.path = path;
            this.name = name;
        }

        public String getPath() {
            return path;
        }

        public String getName() {
            return name;
        }

        public JSONObject toJSON() {
            JSONObject json = new JSONObject();
            try {
                json.put("message", getId());
                json.put("path", path);
                json.put("name", name);

                Throwable cause = getCause();
                if (cause != null && cause instanceof GadgetResourceException)
                    json.put("cause", ((GadgetResourceException) cause).getId());
            } catch (JSONException ex) {
                // ignore
            }

            return json;
        }
    }
}