org.vosao.business.impl.plugin.PluginLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.vosao.business.impl.plugin.PluginLoader.java

Source

/**
 * Vosao CMS. Simple CMS for Google App Engine.
 * 
 * Copyright (C) 2009-2010 Vosao development team.
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * email: vosao.dev@gmail.com
 */

package org.vosao.business.impl.plugin;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.vosao.business.Business;
import org.vosao.common.PluginException;
import org.vosao.dao.Dao;
import org.vosao.entity.FileEntity;
import org.vosao.entity.FolderEntity;
import org.vosao.entity.PluginEntity;
import org.vosao.entity.PluginResourceEntity;
import org.vosao.utils.FolderUtil;
import org.vosao.utils.MimeType;
import org.vosao.utils.StrUtil;

public class PluginLoader {

    private static final Log logger = LogFactory.getLog(PluginLoader.class);

    private static final String VOSAO_PLUGIN = "WEB-INF/vosao-plugin.xml";
    private static final String RESOURCE_LIST = ".resourceList";
    private static final String FILE_LIST = ".fileList";

    private Dao dao;
    private Business business;

    public PluginLoader(Dao dao, Business business) {
        super();
        this.dao = dao;
        this.business = business;
    }

    private static class WarItem {
        public String path;
        public String filename;
        public ByteArrayOutputStream data;

        public WarItem(String path, ByteArrayOutputStream data) {
            super();
            this.path = path;
            this.filename = FolderUtil.getFileName(path);
            this.data = data;
        }
    }

    /**
     * Plugin installation:
     * - PluginEntity created
     * - All resources are placed to /plugins/PLUGIN_NAME/
     * - all classes are placed to PluginResourceEntity
     */
    public void install(String filename, byte[] data) throws IOException, PluginException, DocumentException {
        Map<String, WarItem> war = readWar(data);
        if (!war.containsKey(VOSAO_PLUGIN)) {
            throw new PluginException(VOSAO_PLUGIN + " not found");
        }
        PluginEntity plugin = readPluginConfig(war.get(VOSAO_PLUGIN));
        if (StringUtils.isEmpty(plugin.getEntryPointClass())) {
            throw new PluginException("Entry point class not defined.");
        }
        PluginEntity p = getDao().getPluginDao().getByName(plugin.getName());
        if (p != null) {
            plugin.setConfigData(p.getConfigData());
            uninstall(p);
        }
        getDao().getPluginDao().save(plugin);
        String pluginBase = "/plugins/" + plugin.getName();
        getBusiness().getFolderBusiness().createFolder(pluginBase);
        List<String> resourceList = new ArrayList<String>();
        List<String> fileCacheList = new ArrayList<String>();
        String filePrefix = pluginBase + "/";
        for (String url : war.keySet()) {
            if (!url.equals(VOSAO_PLUGIN)) {
                WarItem item = war.get(url);
                byte[] fileData = item.data.toByteArray();
                if (url.startsWith("WEB-INF/classes")) {
                    resourceList.add(loadClasspathResource(item, plugin));
                }
                if (!url.startsWith("WEB-INF")) {
                    String filePath = filePrefix + item.path;
                    fileCacheList.add(filePath);
                    String folderPath = pluginBase + "/" + FolderUtil.getFilePath(item.path);
                    FolderEntity folder = getBusiness().getFolderBusiness().createFolder(folderPath);
                    FileEntity file = new FileEntity(item.filename, item.filename, folder.getId(),
                            MimeType.getContentTypeByExt(FolderUtil.getFileExt(item.path)), new Date(),
                            fileData.length);
                    getDao().getFileDao().save(file, fileData);
                    getBusiness().getSystemService().getFileCache().remove(filePath);
                }
                if (url.startsWith("WEB-INF/lib") && url.endsWith(".jar")) {
                    resourceList.addAll(loadJarFile(item, plugin));
                }
            }
        }
        saveResourceList(plugin, resourceList, fileCacheList);
    }

    private String loadClasspathResource(WarItem item, PluginEntity plugin) {
        String ext = FolderUtil.getFileExt(item.path);
        byte[] fileData = item.data.toByteArray();
        String resourceName = item.path.replace("WEB-INF/classes/", "");
        if (ext.equals("class")) {
            resourceName = resourceName.replace('/', '.').replace(".class", "");
        }
        PluginResourceEntity res = getDao().getPluginResourceDao().getByUrl(plugin.getName(), resourceName);
        if (res == null) {
            res = new PluginResourceEntity(plugin.getName(), resourceName, fileData);
        } else {
            res.setContent(fileData);
        }
        getDao().getPluginResourceDao().save(res);
        getBusiness().getPluginResourceBusiness().updateResourceCache(res);
        return res.getId().toString();
    }

    private List<String> loadJarFile(WarItem file, PluginEntity plugin) throws IOException {
        List<String> result = new ArrayList<String>();
        Map<String, WarItem> war = readWar(file.data.toByteArray());
        for (String path : war.keySet()) {
            result.add(loadClasspathResource(war.get(path), plugin));
        }
        return result;
    }

    private void saveResourceList(PluginEntity plugin, List<String> resourceList, List<String> fileList) {
        String resourceListStr = StrUtil.toCSV(resourceList);
        String fileListStr = StrUtil.toCSV(fileList);
        try {
            getDao().getPluginResourceDao().save(new PluginResourceEntity(plugin.getName(),
                    plugin.getName() + RESOURCE_LIST, resourceListStr.getBytes("UTF-8")));
            getDao().getPluginResourceDao().save(new PluginResourceEntity(plugin.getName(),
                    plugin.getName() + FILE_LIST, fileListStr.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    private Map<String, WarItem> readWar(byte[] data) throws IOException {
        ByteArrayInputStream inputData = new ByteArrayInputStream(data);
        ZipInputStream in = new ZipInputStream(inputData);
        Map<String, WarItem> map = new HashMap<String, WarItem>();
        ZipEntry entry;
        byte[] buffer = new byte[4096];
        while ((entry = in.getNextEntry()) != null) {
            if (!entry.isDirectory()) {
                ByteArrayOutputStream itemData = new ByteArrayOutputStream();
                int len = 0;
                while ((len = in.read(buffer)) > 0) {
                    itemData.write(buffer, 0, len);
                }
                WarItem item = new WarItem(entry.getName(), itemData);
                map.put(entry.getName(), item);
            }
        }
        in.close();
        return map;
    }

    private PluginEntity readPluginConfig(WarItem zipItem) throws UnsupportedEncodingException, DocumentException {
        PluginEntity result = new PluginEntity();
        Element root = DocumentHelper.parseText(zipItem.data.toString("UTF-8")).getRootElement();
        result.setName(root.elementText("name"));
        result.setTitle(root.elementText("title"));
        result.setVersion(root.elementText("version"));
        result.setDescription(root.elementText("description"));
        result.setWebsite(root.elementText("website"));
        if (root.element("entry-point-class") != null) {
            result.setEntryPointClass(StringUtils.strip(root.elementText("entry-point-class")));
        }
        if (root.element("plugin-config-url") != null) {
            result.setConfigURL(StringUtils.strip(root.elementText("plugin-config-url")));
        }
        StringBuffer header = new StringBuffer();
        if (root.element("header-javascript") != null) {
            for (Element e : (List<Element>) root.elements("header-javascript")) {
                header.append("<script type=\"text/javascript\" src=\"/file/plugins/").append(result.getName())
                        .append("/").append(e.getText()).append("\"></script>\n");
            }
        }
        if (root.element("header-css") != null) {
            for (Element e : (List<Element>) root.elements("header-css")) {
                header.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"/file/plugins/")
                        .append(result.getName()).append("/").append(e.getText()).append("\"/>\n");
            }
        }
        result.setPageHeader(header.toString());
        if (root.element("config") != null) {
            result.setConfigStructure(root.element("config").asXML());
        }
        return result;
    }

    public void uninstall(PluginEntity plugin) {
        removePluginResources(plugin);
        removePluginFileCache(plugin);
        getBusiness().getFolderBusiness().recursiveRemove("/plugins/" + plugin.getName());
        getDao().getPluginDao().remove(plugin.getId());
    }

    private void removePluginResources(PluginEntity plugin) {
        PluginResourceEntity listResource = getDao().getPluginResourceDao().getByUrl(plugin.getName(),
                plugin.getName() + RESOURCE_LIST);
        if (listResource == null) {
            return;
        }
        List<Long> ids = new ArrayList<Long>();
        ids.add(listResource.getId());
        if (listResource.getContent() != null && listResource.getContent().length > 0) {
            try {
                String list = new String(listResource.getContent(), "UTF-8");
                String[] resources = list.split(",");
                for (String id : resources) {
                    ids.add(Long.valueOf(id));
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        getDao().getPluginResourceDao().remove(ids);
    }

    private void removePluginFileCache(PluginEntity plugin) {
        PluginResourceEntity listResource = getDao().getPluginResourceDao().getByUrl(plugin.getName(),
                plugin.getName() + FILE_LIST);
        if (listResource == null) {
            return;
        }
        getDao().getPluginResourceDao().remove(listResource.getId());
        try {
            String list = new String(listResource.getContent(), "UTF-8");
            String[] resources = list.split(",");
            for (String path : resources) {
                getBusiness().getSystemService().getFileCache().remove(path);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public Dao getDao() {
        return dao;
    }

    public void setDao(Dao dao) {
        this.dao = dao;
    }

    public Business getBusiness() {
        return business;
    }

    public void setBusiness(Business business) {
        this.business = business;
    }

}