Java tutorial
/* * 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; } }