com.github.maoo.indexer.webscripts.NodeChangesWebScript.java Source code

Java tutorial

Introduction

Here is the source code for com.github.maoo.indexer.webscripts.NodeChangesWebScript.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.github.maoo.indexer.webscripts;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;

import com.github.maoo.indexer.dao.IndexingDaoImpl;
import com.github.maoo.indexer.entities.Filters;
import com.github.maoo.indexer.entities.NodeEntity;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.TemplateHashModel;

/**
 * Renders out a list of nodes (UUIDs) that have been changed in Alfresco; the changes can affect:
 * - A node metadata
 * - Node content
 * - Node ACLs
 *
 * Please check src/main/amp/config/alfresco/extension/templates/webscripts/com/github/maoo/indexer/webscripts/changes.get.desc.xml
 * to know more about the RestFul interface to invoke the WebScript
 *
 * List of pending activities (or TODOs)
 * - Move private/static logic into the IndexingService
 * - Using JSON libraries (or StringBuffer), render out the payload without passing through FreeMarker template
 * - Wrap (or Proxy) IndexingDaoImpl into an IndexingService, which (optionally) performs any object manipulation
 */
public class NodeChangesWebScript extends DeclarativeWebScript {

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

    @Override
    protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache) {

        final String serviceContextPath = req.getServerPath() + req.getServiceContextPath();

        //start time
        long startTime = System.currentTimeMillis();

        //Fetching request params
        Map<String, String> templateArgs = req.getServiceMatch().getTemplateVars();
        String storeId = templateArgs.get("storeId");
        String storeProtocol = templateArgs.get("storeProtocol");
        String lastTxnIdString = req.getParameter("lastTxnId");
        String lastAclChangesetIdString = req.getParameter("lastAclChangesetId");
        String maxTxnsString = req.getParameter("maxTxns");
        String maxAclChangesetsString = req.getParameter("maxAclChangesets");

        //Parsing parameters passed from the WebScript invocation
        Long lastTxnId = (lastTxnIdString == null ? null : Long.valueOf(lastTxnIdString));
        Long lastAclChangesetId = (lastAclChangesetIdString == null ? null
                : Long.valueOf(lastAclChangesetIdString));
        Integer maxTxns = (maxTxnsString == null ? maxNodesPerTxns : Integer.valueOf(maxTxnsString));
        Integer maxAclChangesets = (maxAclChangesetsString == null ? maxNodesPerAcl
                : Integer.valueOf(maxAclChangesetsString));

        JSONObject indexingFilters = null;
        try {
            indexingFilters = req.getParameter("indexingFilters") != null
                    ? (JSONObject) JSONValue.parse(URLDecoder.decode(req.getParameter("indexingFilters"), "UTF-8"))
                    : null;
        } catch (UnsupportedEncodingException e) {
            throw new WebScriptException(e.getMessage(), e);
        }

        logger.debug(String.format("Invoking Changes Webscript, using the following params\n" + "lastTxnId: %s\n"
                + "lastAclChangesetId: %s\n" + "storeId: %s\n" + "storeProtocol: %s\n" + "indexingFilters: %s\n",
                lastTxnId, lastAclChangesetId, storeId, storeProtocol, indexingFilters));

        //Indexing filters
        Filters filters = null;
        if (indexingFilters != null)
            filters = this.getIndexingFilters(indexingFilters);

        //Getting the Store ID on which the changes are requested
        Pair<Long, StoreRef> store = nodeDao.getStore(new StoreRef(storeProtocol, storeId));
        if (store == null) {
            throw new IllegalArgumentException("Invalid store reference: " + storeProtocol + "://" + storeId);
        }

        Set<NodeEntity> nodes = new HashSet<NodeEntity>();
        //Updating the last IDs being processed
        //Depending on params passed to the request, results will be rendered out
        if (lastTxnId == null) {
            lastTxnId = new Long(0);
        }
        List<NodeEntity> nodesFromTxns = indexingService.getNodesByTransactionId(store, lastTxnId, maxTxns,
                filters);
        if (nodesFromTxns != null && nodesFromTxns.size() > 0) {
            nodes.addAll(nodesFromTxns);
        }

        //Set the last database transaction ID or increment it by maxTxns
        Long lastTxnIdDB = indexingService.getLastTransactionID();

        if ((lastTxnId + maxTxns) > lastTxnIdDB) {
            lastTxnId = lastTxnIdDB;
        } else {
            lastTxnId += maxTxns;
        }

        if (lastAclChangesetId == null) {
            lastAclChangesetId = new Long(0);
        }
        List<NodeEntity> nodesFromAcls = indexingService.getNodesByAclChangesetId(store, lastAclChangesetId,
                maxAclChangesets, filters);
        if (nodesFromAcls != null && nodesFromAcls.size() > 0) {
            nodes.addAll(nodesFromAcls);
        }

        //Set the last database aclChangeSet ID or increment it by maxAclChangesets
        Long lastAclChangesetIdDB = indexingService.getLastAclChangeSetID();

        if ((lastAclChangesetId + maxAclChangesets) > lastAclChangesetIdDB) {
            lastAclChangesetId = lastAclChangesetIdDB;
        } else {
            lastAclChangesetId += maxAclChangesets;
        }

        //elapsed time
        long elapsedTime = System.currentTimeMillis() - startTime;

        //Render them out
        Map<String, Object> model = new HashMap<String, Object>(1, 1.0f);
        model.put("qnameDao", qnameDao);
        model.put("nsResolver", namespaceService);
        model.put("nodes", nodes);
        model.put("lastTxnId", lastTxnId);
        model.put("lastAclChangesetId", lastAclChangesetId);
        model.put("storeId", storeId);
        model.put("storeProtocol", storeProtocol);
        model.put("serviceContextPath", serviceContextPath);
        model.put("propertiesUrlPrefix", propertiesUrlPrefix);
        model.put("elapsedTime", elapsedTime);

        //This allows to call the static method QName.createQName from the FTL template
        try {
            BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
            TemplateHashModel staticModels = wrapper.getStaticModels();
            TemplateHashModel qnameStatics = (TemplateHashModel) staticModels
                    .get("org.alfresco.service.namespace.QName");
            model.put("QName", qnameStatics);
        } catch (Exception e) {
            throw new AlfrescoRuntimeException(
                    "Cannot add BeansWrapper for static QName.createQName method to be used from a Freemarker template",
                    e);
        }

        logger.debug(String.format("Attaching %s nodes to the WebScript template", nodes.size()));

        return model;
    }

    @SuppressWarnings("unchecked")
    private Filters getIndexingFilters(JSONObject indexingParams) {
        Filters filters = new Filters();

        //Types filter
        List<String> types = (List<String>) indexingParams.get("typeFilters");

        if (types != null)
            for (String type : types) {
                filters.addType(this.toQName(type).toString());
            }

        //Site filter
        List<String> sites = (List<String>) indexingParams.get("siteFilters");

        if (sites != null && sites.size() > 0) {
            filters.addSites(sites);
        }

        //Mymetype filter
        List<String> mimetypes = (List<String>) indexingParams.get("mimetypeFilters");

        if (mimetypes != null && mimetypes.size() > 0) {
            filters.addMimeTypes(mimetypes);
        }

        //Aspect filter
        List<String> aspects = (List<String>) indexingParams.get("aspectFilters");

        if (aspects != null)
            for (String aspect : aspects) {
                filters.addAspect(this.toQName(aspect).toString());
            }

        //Metadata filter
        Map<String, String> auxMap = (Map<String, String>) indexingParams.get("metadataFilters");

        if (auxMap != null)
            for (Entry<String, String> entry : auxMap.entrySet()) {
                filters.addMetadata(this.toQName(entry.getKey()).toString(), entry.getValue());
            }

        return filters;
    }

    private QName toQName(String qname) {
        if (qname != null && qname.length() > 0 && qname.charAt(0) != QName.NAMESPACE_BEGIN) {
            return QName.createQName(qname, this.namespaceService);
        } else {
            return QName.createQName(qname);
        }
    }

    private NamespaceService namespaceService;
    private QNameDAO qnameDao;
    private IndexingDaoImpl indexingService;
    private NodeDAO nodeDao;

    private String propertiesUrlPrefix;
    private int maxNodesPerAcl = 1000;
    private int maxNodesPerTxns = 1000;

    public void setNamespaceService(NamespaceService namespaceService) {
        this.namespaceService = namespaceService;
    }

    public void setQnameDao(QNameDAO qnameDao) {
        this.qnameDao = qnameDao;
    }

    public void setIndexingService(IndexingDaoImpl indexingService) {
        this.indexingService = indexingService;
    }

    public void setNodeDao(NodeDAO nodeDao) {
        this.nodeDao = nodeDao;
    }

    public void setPropertiesUrlPrefix(String propertiesUrlPrefix) {
        this.propertiesUrlPrefix = propertiesUrlPrefix;
    }

    public void setMaxNodesPerAcl(int maxNodesPerAcl) {
        this.maxNodesPerAcl = maxNodesPerAcl;
    }

    public void setMaxNodesPerTxns(int maxNodesPerTxns) {
        this.maxNodesPerTxns = maxNodesPerTxns;
    }
}