org.fao.geonet.services.resources.DownloadArchive.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.services.resources.DownloadArchive.java

Source

//=============================================================================
//===   Copyright (C) 2001-2007 Food and Agriculture Organization of the
//===   United Nations (FAO-UN), United Nations World Food Programme (WFP)
//===   and United Nations Environment Programme (UNEP)
//===
//===   This program is free software; you can redistribute it and/or modify
//===   it under the terms of the GNU General Public License as published by
//===   the Free Software Foundation; either version 2 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
//===   General Public License for more details.
//===
//===   You should have received a copy of the GNU General Public License
//===   along with this program; if not, write to the Free Software
//===   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
//===
//===   Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
//===   Rome - Italy. email: geonetwork@osgeo.org
//==============================================================================

package org.fao.geonet.services.resources;

import jeeves.exceptions.BadParameterEx;
import jeeves.exceptions.ResourceNotFoundEx;
import jeeves.interfaces.Service;
import jeeves.resources.dbms.Dbms;
import jeeves.server.ServiceConfig;
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
import jeeves.utils.BinaryFile;
import jeeves.utils.Util;
import jeeves.utils.Xml;
import org.apache.commons.io.IOUtils;
import org.fao.geonet.GeonetContext;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.constants.Params;
import org.fao.geonet.exceptions.MetadataNotFoundEx;
import org.fao.geonet.kernel.AccessManager;
import org.fao.geonet.kernel.DataManager;
import org.fao.geonet.kernel.MdInfo;
import org.fao.geonet.kernel.mef.MEFLib;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.services.Utils;
import org.fao.geonet.util.MailSender;
import org.jdom.Element;

import javax.annotation.Nonnull;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

//=============================================================================

/** Sends the resource to the client in a zip archive with metadata and license
  */

public class DownloadArchive implements Service {
    private static String FS = File.separator;
    private String stylePath;

    //----------------------------------------------------------------------------
    //---
    //--- Init
    //---
    //----------------------------------------------------------------------------

    public void init(String appPath, ServiceConfig params) throws Exception {
        this.stylePath = appPath + FS + Geonet.Path.STYLESHEETS + FS;
    }

    //----------------------------------------------------------------------------
    //---
    //--- Service
    //---
    //----------------------------------------------------------------------------

    public Element exec(Element params, ServiceContext context) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dm = gc.getBean(DataManager.class);
        Dbms dbms = (Dbms) context.getResourceManager().open(Geonet.Res.MAIN_DB);
        UserSession session = context.getUserSession();

        String id = Utils.getIdentifierFromParameters(params, context);
        String access = Util.getParam(params, Params.ACCESS, Params.Access.PUBLIC);

        //--- resource required is public (thumbnails)
        if (access.equals(Params.Access.PUBLIC)) {
            File dir = new File(Lib.resource.getDir(context, access, id));
            String fname = Util.getParam(params, Params.FNAME);

            if (fname.contains("..")) {
                throw new BadParameterEx("Invalid character found in resource name.", fname);
            }

            File file = new File(dir, fname);
            return BinaryFile.encode(200, file.getAbsolutePath(), false);
        }

        //--- from here on resource required is private datafile(s)

        //--- check if disclaimer for this metadata has been displayed
        Element elData = (Element) session.getProperty(Geonet.Session.FILE_DISCLAIMER);
        if (elData == null) {
            return new Element("response");
        } else {
            String idAllowed = elData.getChildText(Geonet.Elem.ID);
            if (idAllowed == null || !idAllowed.equals(id)) {
                return new Element("response");
            }
        }

        //--- check whether notify is required
        boolean doNotify = false;
        Lib.resource.checkPrivilege(context, id, AccessManager.OPER_DOWNLOAD);
        doNotify = true;

        //--- set username for emails and logs
        String username = session.getUsername();
        if (username == null)
            username = "internet";
        String userId = session.getUserId();

        //--- get feedback/reason for download info passed in & record in 'entered'
        //      String name     = Util.getParam(params, Params.NAME);
        //      String org      = Util.getParam(params, Params.ORG);
        //      String email    = Util.getParam(params, Params.EMAIL);
        //      String comments = Util.getParam(params, Params.COMMENTS);
        Element entered = new Element("entered").addContent(params.cloneContent());

        //--- get logged in user details & record in 'userdetails'
        Element userDetails = new Element("userdetails");
        if (!username.equals("internet")) {
            Element elUser = dbms.select(
                    "SELECT username, surname, name, address, state, zip, country, email, organisation FROM Users WHERE id=?",
                    Integer.valueOf(userId));
            if (elUser.getChild("record") != null) {
                userDetails.addContent(elUser.getChild("record").cloneContent());
            }
        }

        //--- get metadata info
        MdInfo info = dm.getMetadataInfo(dbms, id);

        // set up zip output stream
        File zFile = File.createTempFile(username + "_" + info.uuid, ".zip");
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zFile));

        //--- because often content has already been compressed
        out.setLevel(Deflater.NO_COMPRESSION);

        //--- now add the files chosen from the interface and record in 'downloaded'
        Element downloaded = new Element("downloaded");
        File dir = new File(Lib.resource.getDir(context, access, id));

        @SuppressWarnings("unchecked")
        List<Element> files = params.getChildren(Params.FNAME);
        for (Element elem : files) {
            String fname = elem.getText();

            if (fname.contains("..")) {
                continue;
            }

            File file = new File(dir, fname);
            if (!file.exists())
                throw new ResourceNotFoundEx(file.getAbsolutePath());

            Element fileInfo = new Element("file");

            Element details = BinaryFile.encode(200, file.getAbsolutePath(), false);
            String remoteURL = details.getAttributeValue("remotepath");
            if (remoteURL != null) {
                if (context.isDebug())
                    context.debug("Downloading " + remoteURL + " to archive " + zFile.getName());
                fileInfo.setAttribute("size", "unknown");
                fileInfo.setAttribute("datemodified", "unknown");
                fileInfo.setAttribute("name", remoteURL);
                notifyAndLog(doNotify, id, info.uuid, access, username,
                        remoteURL + " (local config: " + file.getAbsolutePath() + ")", context);
                fname = details.getAttributeValue("remotefile");
            } else {
                if (context.isDebug())
                    context.debug("Writing " + fname + " to archive " + zFile.getName());
                fileInfo.setAttribute("size", file.length() + "");
                fileInfo.setAttribute("name", fname);
                Date date = new Date(file.lastModified());
                fileInfo.setAttribute("datemodified", sdf.format(date));
                notifyAndLog(doNotify, id, info.uuid, access, username, file.getAbsolutePath(), context);
            }
            addFile(out, file.getAbsolutePath(), details, fname);
            downloaded.addContent(fileInfo);
        }

        //--- get metadata
        boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = false;
        Element elMd = dm.getMetadata(context, id, forEditing, withValidationErrors, keepXlinkAttributes);

        if (elMd == null)
            throw new MetadataNotFoundEx("Metadata not found - deleted?");

        //--- transform record into brief version
        String briefXslt = stylePath + Geonet.File.METADATA_BRIEF;
        Element elBrief = Xml.transform(elMd, briefXslt);

        //--- create root element for passing all the info we've gathered 
        //--- to license annex xslt generator
        Element root = new Element("root");
        elBrief.setAttribute("changedate", info.changeDate);
        elBrief.setAttribute("currdate", now());
        root.addContent(elBrief);
        root.addContent(downloaded);
        root.addContent(entered);
        root.addContent(userDetails);
        if (context.isDebug())
            context.debug("Passed to metadata-license-annex.xsl:\n " + Xml.getString(root));

        //--- create the license annex html file using the info in root element and
        //--- add it to the zip stream
        String licenseAnnexXslt = stylePath + Geonet.File.LICENSE_ANNEX_XSL;
        File licenseAnnex = File.createTempFile(username + "_" + info.uuid, ".annex");
        FileOutputStream las = new FileOutputStream(licenseAnnex);
        Xml.transform(root, licenseAnnexXslt, las);
        las.close();
        InputStream in = null;
        try {
            in = new FileInputStream(licenseAnnex);
            addFile(out, Geonet.File.LICENSE_ANNEX, in);
        } finally {
            IOUtils.closeQuietly(in);
        }

        //--- if a license is specified include any license files mirrored locally 
        //--- for inclusion
        includeLicenseFiles(context, out, root);

        //--- export the metadata as a partial mef/zip file and add that to the zip
        //--- stream FIXME: some refactoring required here to avoid metadata 
        //--- being read yet again(!) from the database by the MEF exporter
        String outmef = MEFLib.doExport(context, info.uuid, MEFLib.Format.PARTIAL.toString(), false, true, true);
        FileInputStream in2 = null;
        try {
            in2 = new FileInputStream(outmef);
            addFile(out, "metadata.zip", in2);
        } finally {
            IOUtils.closeQuietly(in2);
        }

        //--- now close the zip file and send it out
        if (out != null)
            out.close();
        return BinaryFile.encode(200, zFile.getAbsolutePath(), true);

    }

    //----------------------------------------------------------------------------
    //---
    //--- Private Methods
    //---
    //----------------------------------------------------------------------------

    private void includeLicenseFiles(ServiceContext context, ZipOutputStream out, Element root)
            throws Exception, MalformedURLException, FileNotFoundException, IOException {
        Element license = Xml.selectElement(root, "metadata/*/licenseLink");
        if (license != null) {
            String licenseURL = license.getText();
            if (context.isDebug())
                context.debug("license URL = " + licenseURL);

            String licenseFilesPath = getLicenseFilesPath(licenseURL, context);
            if (context.isDebug())
                context.debug(" licenseFilesPath = " + licenseFilesPath);

            if (licenseFilesPath != null) {
                File licenseFilesDir = new File(licenseFilesPath);
                File[] licenseFiles = licenseFilesDir.listFiles();
                if (licenseFiles == null)
                    return;
                for (File licenseFile : licenseFiles) {
                    if (context.isDebug())
                        context.debug("adding " + licenseFile.getAbsolutePath() + " to zip file");
                    InputStream in = new FileInputStream(licenseFile);
                    addFile(out, licenseFile.getName(), in);
                }
            }
        }
    }

    //----------------------------------------------------------------------------

    private String getLicenseFilesPath(String licenseURL, ServiceContext context) throws MalformedURLException {
        // TODO: Ideally this method should probably read an xml file to map
        // license url's to the sub-directory containing mirrored files
        // but for the moment we'll just use the url path to identify this

        //--- Get license files subdirectory for license
        URL url = new URL(licenseURL);
        String licenseFilesPath = url.getHost() + url.getPath();
        if (context.isDebug())
            context.debug("licenseFilesPath= " + licenseFilesPath);

        //--- Get local mirror directory for license files
        String path = context.getAppPath();
        if (context.isDebug())
            context.debug("path= " + path);

        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        ServiceConfig configHandler = gc.getBean(ServiceConfig.class);
        String licenseDir = configHandler.getValue(Geonet.Config.LICENSE_DIR);
        if (context.isDebug())
            context.debug("licenseDir= " + licenseDir);
        if (licenseDir == null)
            return null;

        File directory = new File(licenseDir);
        if (!directory.isAbsolute())
            licenseDir = path + licenseDir;

        //--- return license files directory
        return licenseDir + '/' + licenseFilesPath;
    }

    //----------------------------------------------------------------------------

    private void addFile(ZipOutputStream zos, String path, Element details, String name) throws Exception {
        ZipEntry entry = new ZipEntry(name);
        zos.putNextEntry(entry);
        BinaryFile.write(details, zos);
        zos.closeEntry();
    }

    //----------------------------------------------------------------------------

    private void addFile(ZipOutputStream zos, String name, @Nonnull InputStream in) throws IOException {
        ZipEntry entry = null;
        try {
            entry = new ZipEntry(name);
            zos.putNextEntry(entry);
            BinaryFile.copy(in, zos);
        } finally {
            try {
                if (zos != null) {
                    zos.closeEntry();
                }
            } finally {
                IOUtils.closeQuietly(in);
            }
        }
    }

    //----------------------------------------------------------------------------

    private static String now() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(cal.getTime());
    }

    //----------------------------------------------------------------------------

    private void notifyAndLog(boolean doNotify, String id, String uuid, String access, String username,
            String theFile, ServiceContext context) throws Exception {

        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        SettingManager sm = gc.getBean(SettingManager.class);
        DataManager dm = gc.getBean(DataManager.class);
        Dbms dbms = (Dbms) context.getResourceManager().open(Geonet.Res.MAIN_DB);

        //--- increase metadata popularity
        if (access.equals(Params.Access.PRIVATE))
            dm.increasePopularity(context, id);

        //--- send email notification
        if (doNotify) {

            String site = sm.getValue("system/site/siteId");
            String host = sm.getValue("system/feedback/mailServer/host");
            String port = sm.getValue("system/feedback/mailServer/port");
            String from = sm.getValue("system/feedback/email");

            String fromDescr = "GeoNetwork administrator";

            String dateTime = now();
            context.info("DOWNLOADED:" + theFile + "," + id + "," + uuid + "," + context.getIpAddress() + ","
                    + username);

            if (host.trim().length() == 0 || from.trim().length() == 0) {
                if (context.isDebug())
                    context.debug("Skipping email notification");
            } else {
                if (context.isDebug())
                    context.debug("Sending email notification for file : " + theFile);

                // send emails about downloaded file to groups with notify privilege
                StringBuffer query = new StringBuffer();
                query.append("SELECT g.id, g.name, g.email ");
                query.append("FROM   OperationAllowed oa, Groups g ");
                query.append("WHERE  oa.operationId =" + AccessManager.OPER_NOTIFY + " ");
                query.append("AND    oa.metadataId = ?");
                query.append("AND    oa.groupId = g.id");

                Element groups = dbms.select(query.toString(), Integer.valueOf(id));
                @SuppressWarnings("unchecked")
                List<Element> groupsEls = groups.getChildren();
                for (Element group : groupsEls) {
                    String name = group.getChildText("name");
                    String email = group.getChildText("email");

                    if (email.trim().length() != 0) {
                        String subject = "File " + theFile + " has been downloaded at " + dateTime;
                        String message = "GeoNetwork (site: " + site + ") notifies you, as supervisor of group "
                                + name + " that data file " + theFile
                                + " attached to metadata record with id number " + id + " (uuid: " + uuid + ")"
                                + " has been downloaded from address " + context.getIpAddress() + " by user "
                                + username + ".";

                        try {
                            MailSender sender = new MailSender(context);
                            sender.send(host, Integer.parseInt(port), from, fromDescr, email, null, subject,
                                    message);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

}

//=============================================================================