/*
* Copyright 2012 Esri.
*
* Licensed 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.esri.gpt.server.erosfeed;
import com.esri.gpt.catalog.discovery.rest.RestQuery;
import com.esri.gpt.catalog.discovery.rest.RestQueryParser;
import com.esri.gpt.catalog.management.MmdEnums.ApprovalStatus;
import com.esri.gpt.catalog.search.*;
import com.esri.gpt.control.georss.AtomFeedWriter;
import com.esri.gpt.control.georss.FeedWriter;
import com.esri.gpt.control.georss.RecordSnippetWriter;
import com.esri.gpt.control.georss.RestQueryServlet;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.isodate.IsoDateFormat;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.security.identity.IdentityAdapter;
import com.esri.gpt.framework.security.principal.RoleSet;
import com.esri.gpt.framework.sql.BaseDao;
import com.esri.gpt.framework.util.Val;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.sql.*;
import java.text.ParseException;
import java.util.Date;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Eros query servlet.
*/
public class ErosQueryServlet extends RestQueryServlet {
private String defaultQuery = "";
private Map<String,String[]> defaultParameters = new HashMap<String, String[]>();
private IsoDateFormat dateFormat = new IsoDateFormat(IsoDateFormat.Format.basic);
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
defaultQuery = Val.chkStr(config.getInitParameter("defaultQuery"),"");
defaultParameters = parseParameters(defaultQuery);
}
@Override
protected void execute(HttpServletRequest request, HttpServletResponse response, RequestContext context) throws Exception {
RoleSet roleSet = new RoleSet();
roleSet.add("gptAdministrator");
context.getUser().getAuthenticationStatus().authorizeAction(roleSet);
super.execute(request, response, context);
}
@Override
protected RestQuery parseRequest(HttpServletRequest request, RequestContext context) {
RestQuery query = new RestQuery();
RestQueryParser parser = new ErosQueryParser(request,context,query,defaultParameters);
parser.parseResponseFormat("f");
String requestURI = Val.chkStr(request.getRequestURI());
if (requestURI.toLowerCase().endsWith("/sitemap")) {
String tmp = Val.chkStr(query.getResponseFormat());
if (tmp.length() == 0) {
query.setResponseFormat("sitemap");
}
}
parser.parseRepositoryId("rid");
parser.parseResponseFormat("f");
parser.parseResponseGeometry("geometryType");
parser.parseResponseStyle("style");
parser.parseResponseTarget("target");
parser.parseStartRecord("start",1);
parser.parseMaxRecords("max",10);
parser.parsePropertyIsEqualTo("uuid","uuid");
parser.parsePropertyIsLike("searchText","anytext");
parser.parsePropertyList("contentType","dc:type",",",true);
parser.parsePropertyList("dataCategory","dc:subject",",",true);
parser.parsePropertyRange("after","before","dct:modified");
parser.parseSpatialClause("bbox","spatialRel","geometry");
parser.parseSortables("orderBy");
//parser.parsePropertyRange("validAfter","validBefore","dct:valid"); // date valid
parser.parsePropertyIsEqualTo("publisher","dc:publisher"); // publisher
parser.parsePropertyIsEqualTo("source","dc:source"); // harvesting id (uuid)
parser.parsePropertyIsEqualTo("isPartOf","dct:isPartOf"); // collection subset
//parser.parsePropertyList("hasFormat","dct:hasFormat",",",true);
return query;
}
private Date extractDate(HttpServletRequest request, String paramName) {
Date date = null;
String paramValue = Val.chkStr(request.getParameter(paramName));
if (!paramValue.isEmpty()) {
try {
date = dateFormat.parseObject(paramValue);
} catch (ParseException ex) {}
}
return date;
}
@Override
protected SearchResult executeQuery1(HttpServletRequest request, RequestContext context, MessageBroker messageBroker, RestQuery query) throws SearchException {
Date after = extractDate(request, "after");
Date before = extractDate(request, "before");
SearchResult result = executeRepoQuery(request, context, messageBroker, query, after, before);
int maxRecords = query.getFilter().getMaxRecords();
if (result.getRecordSize()>0) {
query.getFilter().setStartRecord(1);
} else {
query.getFilter().setStartRecord(Math.max(1,query.getFilter().getStartRecord()-result.getRecords().getOpenSearchProperties().getNumberOfHits()));
}
query.getFilter().setMaxRecords(query.getFilter().getMaxRecords()-result.getRecordSize());
SearchResult recResult = super.executeQuery1(request, context, messageBroker, query);
if (result.getRecordSize()<maxRecords) {
result.getRecords().addAll(recResult.getRecords());
}
result.setSearchTimeInSeconds(result.getSearchTimeInSeconds()+recResult.getSearchTimeInSeconds());
result.getRecords().getOpenSearchProperties().setNumberOfHits(result.getRecords().getOpenSearchProperties().getNumberOfHits()+recResult.getRecords().getOpenSearchProperties().getNumberOfHits());
//result.getRecords().getOpenSearchProperties().setRecordsPerPage(result.getRecords().size());
return result;
}
protected SearchResult executeRepoQuery(HttpServletRequest request, RequestContext context, MessageBroker messageBroker, RestQuery query, Date after, Date before) throws SearchException {
ResourceIdentifier resourceIdentifier = ResourceIdentifier.newIdentifier(context);
SearchResult result = new SearchResult();
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
StringBuilder sb = new StringBuilder();
sb.append("SELECT DOCUUID, TITLE, HOST_URL, PROTOCOL_TYPE, PROTOCOL FROM ").append(context.getCatalogConfiguration().getResourceTableName());
sb.append(" WHERE PROTOCOL_TYPE IS NOT NULL");
if (BaseDao.getIsDbCaseSensitive(context)) {
sb.append(" AND (UPPER(APPROVALSTATUS)='").append(ApprovalStatus.approved.name().toUpperCase()).append("' OR UPPER(APPROVALSTATUS)='").append(ApprovalStatus.reviewed.name().toUpperCase()).append("')");
} else {
sb.append(" AND (APPROVALSTATUS='").append(ApprovalStatus.approved.name()).append("' OR APPROVALSTATUS='").append(ApprovalStatus.reviewed.name()).append("')");
}
if (after!=null) {
sb.append(" AND UPDATEDATE>=?");
}
if (before!=null) {
sb.append(" AND UPDATEDATE<=?");
}
try {
conn = context.getConnectionBroker().returnConnection("").getJdbcConnection();
st = conn.prepareStatement(sb.toString());
int n = 0;
if (after!=null) {
st.setTimestamp(++n, new Timestamp(after.getTime()));
}
if (before!=null) {
st.setTimestamp(++n, new Timestamp(before.getTime()));
}
rs = st.executeQuery();
ArrayList<SearchResultRecord> hrAcceptedRecords = new ArrayList<SearchResultRecord>();
while (rs.next()) {
String uuid = Val.chkStr(rs.getString("DOCUUID"));
String name = Val.chkStr(rs.getString("TITLE"));
String url = Val.chkStr(rs.getString("HOST_URL"));
String kind = Val.chkStr(rs.getString("PROTOCOL_TYPE")).toLowerCase();
kind = decodeKind(resourceIdentifier, kind, url);
if (Val.chkStr(kind).isEmpty()) {
continue;
}
SearchResultRecord record = new SearchResultRecord();
record.setUuid(uuid);
record.setTitle(name);
record.setServiceType(kind);
record.setResourceUrl(url);
hrAcceptedRecords.add(record);
}
for (int i=query.getFilter().getStartRecord()-1; i<hrAcceptedRecords.size() && result.getRecordSize()<query.getFilter().getMaxRecords(); i++) {
SearchResultRecord record = hrAcceptedRecords.get(i);
result.getRecords().add(record);
}
String basePath = RequestContext.resolveBaseContextPath(request);
String osURL = basePath+"/openSearchDescription";
OpenSearchProperties osProps = new OpenSearchProperties();
osProps.setShortName(messageBroker.retrieveMessage("catalog.openSearch.shortName"));
osProps.setDescriptionURL(osURL);
osProps.setNumberOfHits(hrAcceptedRecords.size());
osProps.setStartRecord(query.getFilter().getStartRecord());
osProps.setRecordsPerPage(query.getFilter().getMaxRecords());
result.getRecords().setOpenSearchProperties(osProps);
return result;
} catch (SQLException ex) {
throw new SearchException("Error searching records", ex);
} finally {
if (rs!=null) {
try {
rs.close();
} catch (SQLException ex) {
}
}
if (st!=null) {
try {
st.close();
} catch (SQLException ex) {
}
}
context.getConnectionBroker().closeAll();
}
}
protected String decodeKind(ResourceIdentifier resourceIdentifier, String kind, String url) {
if ("arcgis".equals(kind)) {
kind = "ags";
} else if ("res".equals(kind)) {
kind = resourceIdentifier.guessServiceTypeFromUrl(url);
if (kind.isEmpty()) {
return null;
}
} else if ("waf".equals(kind)) {
} else if ("csw".equals(kind)) {
} else {
return null;
}
return kind;
}
@Override
protected FeedWriter makeFeedWriter(HttpServletRequest request, RequestContext context, PrintWriter printWriter, MessageBroker messageBroker, RestQuery query) {
ResponseFormat format = getResponseFormat(request,query);
if (format.equals(ResponseFormat.atom)){
ResourceIdentifier resourceIdentifier = ResourceIdentifier.newIdentifier(context);
IdentityAdapter idAdapter = context.newIdentityAdapter();
ErosEmailFinder emailFinder = new ErosEmailFinder(context, idAdapter);
String sTarget = query.getResponseTarget();
RecordSnippetWriter.Target target = RecordSnippetWriter.Target.checkValueOf(sTarget);
AtomFeedWriter atomWriter = new ErosAtomFeedWriter(emailFinder, resourceIdentifier, printWriter);
atomWriter.setEntryBaseUrl(query.getRssProviderUrl());
atomWriter.set_messageBroker(messageBroker);
atomWriter.setTarget(target);
return atomWriter;
}
return super.makeFeedWriter(request, context, printWriter, messageBroker, query);
}
private Map<String,String[]> parseParameters(String parameters) {
HashMap<String,ArrayList<String>> parametersMap = new HashMap<String, ArrayList<String>>();
String [] elements = parameters.split("&");
for (String element: elements) {
String [] kvp = element.split("=");
if (kvp!=null && kvp.length>=1) {
ArrayList<String> values = null;
if (parametersMap.containsKey(kvp[0])) {
values = parametersMap.get(kvp[0]);
} else {
values = new ArrayList<String>();
parametersMap.put(kvp[0], values);
}
values.add(kvp.length>=2? decode(kvp[1]): "");
}
}
return castParametersMap(parametersMap);
}
private Map<String,String[]> castParametersMap(Map<String,ArrayList<String>> map) {
TreeMap<String,String[]> parametersMap = new TreeMap<String, String[]>();
for (Map.Entry<String,ArrayList<String>> e: map.entrySet()) {
parametersMap.put(e.getKey(), e.getValue().toArray(new String[e.getValue().size()]));
}
return parametersMap;
}
private String decode(String s) {
try {
return URLDecoder.decode(s, "UTF-8");
} catch (UnsupportedEncodingException ex) {
return s;
}
}
}