Package org.fao.geonet.component.csw

Source Code of org.fao.geonet.component.csw.Transaction

//=============================================================================
//===  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.component.csw;

import com.vividsolutions.jts.util.Assert;
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
import org.fao.geonet.GeonetContext;
import org.fao.geonet.Util;
import org.fao.geonet.constants.Edit;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.csw.common.Csw;
import org.fao.geonet.csw.common.ElementSetName;
import org.fao.geonet.csw.common.OutputSchema;
import org.fao.geonet.csw.common.ResultType;
import org.fao.geonet.csw.common.exceptions.CatalogException;
import org.fao.geonet.csw.common.exceptions.NoApplicableCodeEx;
import org.fao.geonet.domain.Pair;
import org.fao.geonet.domain.Profile;
import org.fao.geonet.domain.ReservedGroup;
import org.fao.geonet.domain.ReservedOperation;
import org.fao.geonet.kernel.*;
import org.fao.geonet.kernel.csw.CatalogService;
import org.fao.geonet.kernel.csw.services.AbstractOperation;
import org.fao.geonet.kernel.csw.services.getrecords.FieldMapper;
import org.fao.geonet.kernel.csw.services.getrecords.SearchController;
import org.fao.geonet.kernel.schema.MetadataSchema;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.utils.Log;
import org.jdom.Element;
import org.jdom.Namespace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.*;

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

/**
* CSW transaction operation.
*/
@Component(CatalogService.BEAN_PREFIX + Transaction.NAME)
public class Transaction extends AbstractOperation implements CatalogService {
    //---------------------------------------------------------------------------
    //---
    //--- Constructor
    //---
    //---------------------------------------------------------------------------

    static final String NAME = "Transaction";
    private SearchController _searchController;
    @Autowired
    private FieldMapper _fieldMapper;
    @Autowired
    SchemaManager _schemaManager;

    @Autowired
    public Transaction(ApplicationContext context) {
        _searchController = new SearchController(context);
    }


    //---------------------------------------------------------------------------
    //---
    //--- API methods
    //---
    //---------------------------------------------------------------------------

    public String getName() {
        return NAME;
    }

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

    public Element execute(Element request, ServiceContext context) throws CatalogException {
        checkService(request);
        checkVersion(request);

        //num counter
        int totalInserted = 0;
        int totalUpdated = 0;
        int totalDeleted = 0;

        // Response element
        Element response = new Element(getName() + "Response", Csw.NAMESPACE_CSW);

        List<String> strFileIds = new ArrayList<String>();

        //process the transaction from the first to the last
        @SuppressWarnings("unchecked")
        List<Element> childList = request.getChildren();

        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dataMan = gc.getBean(DataManager.class);

        Set<String> toIndex = new HashSet<String>();

        try {
            //process the childlist
            for (Element transRequest : childList) {
                String transactionType = transRequest.getName().toLowerCase();
                if (transactionType.equals("insert") || transactionType.equals("update") || transactionType.equals("delete")) {
                    @SuppressWarnings("unchecked")
                    List<Element> mdList = transRequest.getChildren();

                    // insert to database, and get the number of inserted successful
                    if (transactionType.equals("insert")) {
                        for (Element aMdList : mdList) {
                            Element metadata = (Element) aMdList.clone();
                            boolean insertSuccess = insertTransaction(metadata, strFileIds, context, toIndex);
                            if (insertSuccess) {
                                totalInserted++;
                            }
                        }
                    } else if (transactionType.equals("update")) {
                        // Update
                        Element metadata = null;
                        for (Element reqElem : mdList) {
                            if (reqElem.getNamespace() != Csw.NAMESPACE_CSW) {
                                metadata = (Element) reqElem.clone();
                            }
                        }

                        totalUpdated = updateTransaction(transRequest, metadata, context, toIndex);
                    } else {
                        // Delete
                        totalDeleted = deleteTransaction(transRequest, context);
                    }
                }
            }
        } catch (Exception e) {
            Log.error(Geonet.CSW, "Cannot process transaction");
            Log.error(Geonet.CSW, " (C) StackTrace\n" + Util.getStackTrace(e));

            throw new NoApplicableCodeEx("Cannot process transaction: " + e.getMessage());
        } finally {
            try {
                dataMan.indexMetadata(new ArrayList<String>(toIndex));
            } catch (Exception e) {
                Log.error(Geonet.CSW, "cannot index");
                Log.error(Geonet.CSW, " (C) StackTrace\n" + Util.getStackTrace(e));
            }
            getResponseResult(request, response, strFileIds, totalInserted, totalUpdated, totalDeleted);
        }

        return response;
    }

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

    public Element adaptGetRequest(Map<String, String> params) {
        return new Element(getName(), Csw.NAMESPACE_CSW);
    }

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

    public Element retrieveValues(String parameterName) throws CatalogException {
        return null;
    }

    //---------------------------------------------------------------------------
    //---
    //--- Private methods
    //---
    //---------------------------------------------------------------------------

    /**
     * @param xml
     * @param fileIds
     * @param context
     * @param toIndex
     * @return
     * @throws Exception
     */
    private boolean insertTransaction(Element xml, List<String> fileIds, ServiceContext context, Set<String> toIndex) throws Exception {
        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dataMan = gc.getBean(DataManager.class);

        String schema = dataMan.autodetectSchema(xml);

//    String category   = Util.getParam(request, Params.CATEGORY);
        String category = null, source = null, createDate = null, changeDate = null;

        String uuid;
        uuid = dataMan.extractUUID(schema, xml);
        if (uuid.length() == 0)
            uuid = UUID.randomUUID().toString();

        // -----------------------------------------------------------------------
        // --- insert metadata into the system

        UserSession us = context.getUserSession();

        if (us.getUserId() == null)
            throw new NoApplicableCodeEx("User not authenticated.");

        Profile profile = us.getProfile();

        // Only editors and above are allowed to insert metadata
        if (profile != Profile.Editor && profile != Profile.Reviewer
            && profile != Profile.UserAdmin && profile != Profile.Administrator)
            throw new NoApplicableCodeEx("User not allowed to insert metadata.");

        int userId = us.getUserIdAsInt();

        AccessManager am = gc.getBean(AccessManager.class);

        // Set default group: user first group
        Set<Integer> userGroups = am.getVisibleGroups(userId);
        String group;
        if (userGroups.isEmpty()) {
            group = null;
        } else {
            group = userGroups.iterator().next().toString();
        }
        //
        // insert metadata
        //
        String docType = null, title = null, isTemplate = null;
        boolean ufo = true, indexImmediate = false;
        String id = dataMan.insertMetadata(context, schema, xml, uuid, userId, group, source,
                isTemplate, docType, category, createDate, changeDate, ufo, indexImmediate);

        // Privileges for the first group of the user that inserts the metadata
        // (same permissions as when inserting xml file from UI)
        if (group != null) {
            for (ReservedOperation op : ReservedOperation.values()) {
                dataMan.unsetOperation(context, id, group, op);
            }
        }

        // Set metadata as public if setting enabled
        SettingManager sm = gc.getBean(SettingManager.class);
        boolean metadataPublic = sm.getValueAsBool("system/csw/metadataPublic", false);

        if (metadataPublic) {
            dataMan.setOperation(context, id, "" + ReservedGroup.all.getId(), ReservedOperation.view);
        }

        dataMan.indexMetadata(id, false);

        fileIds.add(uuid);

        toIndex.add(id);
        return true;
    }


    /**
     * @param request
     * @param xml
     * @param context
     * @param toIndex
     * @return
     * @throws Exception
     */
    private int updateTransaction(Element request, Element xml, ServiceContext context, Set<String> toIndex) throws Exception {
        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dataMan = gc.getBean(DataManager.class);

        if (context.getUserSession().getUserId() == null) {
            throw new NoApplicableCodeEx("User not authenticated.");
        }

        int totalUpdated = 0;


        // Update full metadata
        if (xml != null) {

            // Retrieve schema and the related Namespaces
            String schemaId = gc.getBean(SchemaManager.class).autodetectSchema(xml);

            if (schemaId == null) {
                throw new NoApplicableCodeEx("Can't identify metadata schema");
            }

            // Retrieve the metadata identifier

            String uuid = gc.getBean(DataManager.class).extractUUID(schemaId, xml);

            if (uuid.length() == 0) {
                throw new NoApplicableCodeEx("Metadata identifier not provided");
            }

            // Update metadata record
            String id = dataMan.getMetadataId(uuid);

            if (id == null) {
                return totalUpdated;
            }

            if (!gc.getBean(AccessManager.class).canEdit(context, id)) {
                throw new NoApplicableCodeEx("User not allowed to update this metadata(" + id + ").");
            }

            String changeDate = null;

            boolean validate = false;
            boolean ufo = false;
            boolean index = false;
            String language = context.getLanguage();
            dataMan.updateMetadata(context, id, xml, validate, ufo, index, language, changeDate, false);

            toIndex.add(id);

            totalUpdated++;

            return totalUpdated;

            // Update properties
        } else {
            //first, search the record in the database to get the record id
            final List<Element> constraints = request.getChildren("Constraint", Csw.NAMESPACE_CSW);
            Assert.isTrue(constraints.size() == 1, "One and only 1 constraint allowed in update query.  Found : " + constraints.size());
            Element constr = (Element) constraints.get(0).clone();
            List<Element> results = getResultsFromConstraints(context, constr);


            @SuppressWarnings("unchecked")
            List<Element> recordProperties = request.getChildren("RecordProperty", Csw.NAMESPACE_CSW);


            Iterator<Element> it = results.iterator();
            if (!it.hasNext())
                return totalUpdated;

            Set<String> updatedMd = new HashSet<String>();
            // Process all records selected
            while (it.hasNext()) {
                Element result = it.next();
                String uuid = result.getChildText("identifier", Csw.NAMESPACE_DC);
                String id = dataMan.getMetadataId(uuid);
                String changeDate = null;

                if (id == null)
                    continue;

                if (!dataMan.getAccessManager().canEdit(context, id))
                    throw new NoApplicableCodeEx("User not allowed to update this metadata(" + id + ").");


                Element metadata = dataMan.getMetadata(context, id, false, false, true);
                metadata.removeChild("info", Edit.NAMESPACE);

                // Retrieve the schema and Namespaces of metadata to update
                String schemaId = gc.getBean(DataManager.class).autodetectSchema(metadata);

                if (schemaId == null) {
                    throw new NoApplicableCodeEx("Can't identify metadata schema");
                }

                boolean metadataChanged = false;
                EditLib editLib = new EditLib(_schemaManager);

                MetadataSchema metadataSchema = _schemaManager.getSchema(schemaId);
                final String settingId = SettingManager.CSW_TRANSACTION_XPATH_UPDATE_CREATE_NEW_ELEMENTS;
                boolean createXpathNodeIfNotExists = gc.getBean(SettingManager.class).getValueAsBool(settingId);

                // Process properties to update
                for (Element recordProperty : recordProperties) {
                    Element propertyNameEl = recordProperty.getChild("Name", Csw.NAMESPACE_CSW);
                    Element propertyValueEl = recordProperty.getChild("Value", Csw.NAMESPACE_CSW);

                    String propertyName = propertyNameEl.getText();

                    // Get XPath for queriable name, i provided in propertyName.
                    // Otherwise assume propertyName contains full XPath to property to update
                    String xpathProperty = _fieldMapper.mapXPath(propertyName, schemaId);
                    if (xpathProperty == null) {
                        xpathProperty = propertyName;
                    }

                    Log.info(Geonet.CSW, "Xpath of property: " + xpathProperty);

                    final List<Element> children = propertyValueEl.getChildren();
                    AddElemValue propertyValue;
                    if (children.isEmpty()) {
                        propertyValue = new AddElemValue(propertyValueEl.getText());
                        metadataChanged |= editLib.addElementOrFragmentFromXpath(metadata, metadataSchema, xpathProperty, propertyValue,
                                createXpathNodeIfNotExists);
                    } else {
                        for (Element child : children) {
                            propertyValue = new AddElemValue((Element) child.clone());
                            metadataChanged |= editLib.addElementOrFragmentFromXpath(metadata, metadataSchema, xpathProperty, propertyValue,
                                    createXpathNodeIfNotExists);
                        }
                    }

                    Log.info(Geonet.CSW, "Metadata has been updated: "+metadataChanged);

                }

                // Update the metadata with changes
                if (metadataChanged) {
                    boolean validate = false;
                    boolean ufo = false;
                    boolean index = false;
                    String language = context.getLanguage();
                    dataMan.updateMetadata(context, id, metadata, validate, ufo, index, language, changeDate, false);

                    updatedMd.add(id);

                    totalUpdated++;
                }

            }
            toIndex.addAll(updatedMd);

            return totalUpdated;

        }
    }

    /**
     * @param request
     * @param context
     * @return
     * @throws Exception
     */
    private int deleteTransaction(Element request, ServiceContext context) throws Exception {
        int deleted = 0;

        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dataMan = gc.getBean(DataManager.class);

        if (context.getUserSession().getUserId() == null)
            throw new NoApplicableCodeEx("User not authenticated.");

        //first, search the record in the database to get the record id
        Element constr = request.getChild("Constraint", Csw.NAMESPACE_CSW);
        List<Element> results = getResultsFromConstraints(context, constr);

        //second, delete the metadata in the dbms using the id
        Iterator<Element> i = results.iterator();
        if (!i.hasNext())
            return deleted;

        // delete all matching records
        while (i.hasNext()) {
            Element result = i.next();
            String uuid = result.getChildText("identifier", Csw.NAMESPACE_DC);
            String id = dataMan.getMetadataId(uuid);

            if (id == null) {
                return deleted;
            }

            if (!dataMan.getAccessManager().canEdit(context, id)) {
                throw new NoApplicableCodeEx("User not allowed to delete metadata : " + id);
            }

            dataMan.deleteMetadata(context, id);
            deleted++;
        }

        return deleted;

    }

    /**
     * @param context
     * @param constr
     * @return
     * @throws CatalogException
     */
    private List<Element> getResultsFromConstraints(ServiceContext context, Element constr) throws CatalogException {
        Element filterExpr = getFilterExpression(constr);
        String filterVersion = getFilterVersion(constr);

        ElementSetName setName = ElementSetName.BRIEF;

        Pair<Element, Element> results = _searchController.search(context, 1, 100, ResultType.RESULTS,
                OutputSchema.OGC_CORE, setName, filterExpr, filterVersion, null, null, null, 0, null, null);

        @SuppressWarnings("unchecked")
        List<Element> children = results.two().getChildren();
        return children;
    }

    /**
     * @param request
     * @param response
     * @param fileIds
     * @param totalInserted
     * @param totalUpdated
     * @param totalDeleted
     */
    private void getResponseResult(Element request, Element response, List<String> fileIds,
                                   int totalInserted, int totalUpdated, int totalDeleted) {
        // transactionSummary
        Element transactionSummary = getTransactionSummary(totalInserted, totalUpdated, totalDeleted);

        // requestID
        String strRequestId = request.getAttributeValue("requestId");
        if (strRequestId != null)
            transactionSummary.setAttribute("requestId", strRequestId);

        response.addContent(transactionSummary);

        if (totalInserted > 0) {
            Element insertResult = new Element("InsertResult", Csw.NAMESPACE_CSW);
            insertResult.setAttribute("handleRef", "handleRefValue");

            //Namespace NAMESPACE_DC = Namespace.getNamespace("dc", "http://purl.org/dc/elements/1.1/");
            for (String fileId : fileIds) {
                Element briefRecord = new Element("BriefRecord", Csw.NAMESPACE_CSW);
                Element identifier = new Element("identifier"); //,Csw.NAMESPACE_DC );
                identifier.setText(fileId);
                briefRecord.addContent(identifier);
                insertResult.addContent(briefRecord);
            }

            response.addContent(insertResult);
        }

    }

    /**
     * @param totalInserted
     * @param totalUpdated
     * @param totalDeleted
     * @return
     */
    private Element getTransactionSummary(int totalInserted, int totalUpdated, int totalDeleted) {
        Element transactionSummary = new Element("TransactionSummary", Csw.NAMESPACE_CSW);
//    if( totalInserted>0 )
//    {     
        Element insert = new Element("totalInserted", Csw.NAMESPACE_CSW);
        insert.setText(Integer.toString(totalInserted));
        transactionSummary.addContent(insert);

//    }
//    if( totalUpdated>0 )
//    {
        Element update = new Element("totalUpdated", Csw.NAMESPACE_CSW);
        update.setText(Integer.toString(totalUpdated));
        transactionSummary.addContent(update);
//    }
//    if( totalDeleted>0 )
//    {
        Element delete = new Element("totalDeleted", Csw.NAMESPACE_CSW);
        delete.setText(Integer.toString(totalDeleted));
        transactionSummary.addContent(delete);
//    }

        return transactionSummary;
    }

    /**
     * Retrieves namespaces based on metadata schema
     *
     * @param mdSchema
     * @return
     * @throws NoApplicableCodeEx
     */
    private Map<String, String> retrieveNamepacesForSchema(MetadataSchema mdSchema) throws NoApplicableCodeEx {
        Map<String, String> mapNs = new HashMap<String, String>();

        List<Namespace> schemaNsList = mdSchema.getSchemaNS();

        for (Namespace ns : schemaNsList) {
            mapNs.put(ns.getPrefix(), ns.getURI());
        }

        return mapNs;
    }

}

//=============================================================================
TOP

Related Classes of org.fao.geonet.component.csw.Transaction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.