/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. 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.esri.gpt.catalog.lucene;
import com.esri.gpt.catalog.arcims.ImsMetadataAdminDao;
import com.esri.gpt.catalog.context.CatalogIndexAdapter;
import com.esri.gpt.catalog.schema.MetadataDocument;
import com.esri.gpt.catalog.schema.Schema;
import com.esri.gpt.catalog.schema.SchemaException;
import com.esri.gpt.framework.collection.StringAttributeMap;
import com.esri.gpt.framework.collection.StringSet;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.security.metadata.MetadataAcl;
import com.esri.gpt.framework.sql.IClobMutator;
import com.esri.gpt.framework.sql.ManagedConnection;
import com.esri.gpt.framework.util.Val;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.lucene.index.IndexWriter;
/**
* Lucene indexing service for a single JVM/IndexWriter combination.
*/
@SuppressWarnings("serial")
public class SingleIndexingServlet extends HttpServlet {
/** class variables ========================================================= */
/** The Logger. */
private static Logger LOGGER = Logger.getLogger(SingleIndexingServlet.class.getName());
/**
* Handles a GET request.
* <p/>
* The default behavior is the execute the doPost method.
* @param request the servlet request
* @param response the servlet response
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request,response);
}
/**
* Handles a POST request.
* @param request the servlet request
* @param response the servlet response
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestContext context = null;
try {
LOGGER.finer("Query string="+request.getQueryString());
String sEncoding = request.getCharacterEncoding();
if ((sEncoding == null) || (sEncoding.trim().length() == 0)) {
request.setCharacterEncoding("UTF-8");
}
context = RequestContext.extract(request);
String action = Val.chkStr(request.getParameter("action"));
if (action.equalsIgnoreCase("isSynchronizerRunning")) {
boolean isRunning = LuceneIndexSynchronizer.RUNNING;
this.writeCharacterResponse(response,
""+isRunning,"UTF-8","text/plain; charset=UTF-8");
return;
}
context.getObjectMap().put("lucene.useRemoteWriter",false);
StringAttributeMap params = context.getCatalogConfiguration().getParameters();
String param = Val.chkStr(params.getValue("lucene.useSingleSearcher"));
boolean useSingleWriter = param.equalsIgnoreCase("true");
param = Val.chkStr(params.getValue("lucene.useLocalWriter"));
boolean bUseLocalWriter = !param.equalsIgnoreCase("false");
param = Val.chkStr(params.getValue("lucene.useRemoteWriter"));
boolean useRemoteWriter = param.equalsIgnoreCase("true");
String remoteWriterUrl = Val.chkStr(params.getValue("lucene.remoteWriterUrl"));
boolean bOk = true;
if (!useSingleWriter || !bUseLocalWriter) {
bOk = false;
String s = "Inconsistent configuration parameters,"+
" lucene.useSingleWriter lucene.useLocalWriter";
LOGGER.severe(s);
response.sendError(500,"Inconsistent configuration parameters on server.");
}
if (bOk) {
String sIds = Val.chkStr(request.getParameter("ids"));
String[] ids = sIds.split(",");
if (action.equalsIgnoreCase("delete")) {
this.executeDelete(request,response,context,ids);
} else if (action.equalsIgnoreCase("publish")) {
this.executePublish(request,response,context,ids);
} else if (action.equalsIgnoreCase("runSynchronizer")) {
StringAttributeMap syncParams = new StringAttributeMap();
//syncParams.set("feedbackSeconds","30");
LuceneIndexSynchronizer lis = new LuceneIndexSynchronizer(syncParams);
lis.syncronize();
} else if (action.equalsIgnoreCase("touch")) {
LuceneIndexAdapter adapter = new LuceneIndexAdapter(context);
adapter.touch();
} else {
String s = "Unrecognized action: "+action;
LOGGER.log(Level.WARNING,s);
}
}
} catch (Throwable t) {
String sErr = "Exception occured while processing servlet request.";
LOGGER.log(Level.SEVERE,sErr,t);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
if (context != null) context.onExecutionPhaseCompleted();
}
}
/**
* Executes an index deletion request.
* @param request the HTTP request
* @param response the HTTP response
* @param context the request context
* @param ids the document ids to delete
* @throws Exception if an exception occurs
*/
protected void executeDelete(HttpServletRequest request,
HttpServletResponse response, RequestContext context, String[] ids)
throws Exception {
if ((ids == null || ids.length == 0)) return;
LuceneIndexAdapter adapter = new LuceneIndexAdapter(context);
IndexWriter writer = null;
try {
if (adapter.getUsingSingleWriter()) {
writer = adapter.newWriter();
adapter.setAutoCommitSingleWriter(false);
}
adapter.deleteDocuments(ids);
} finally {
try {
if ((adapter != null) && (writer != null)) {
adapter.closeWriter(writer);
}
} catch (Exception ef) {
LOGGER.log(Level.WARNING,"IndexWriter failed to close.",ef);
}
}
}
/**
* Executes an index publication request.
* @param request the HTTP request
* @param response the HTTP response
* @param context the request context
* @param ids the document ids to publish
* @throws Exception if an exception occurs
*/
protected void executePublish(HttpServletRequest request,
HttpServletResponse response, RequestContext context, String[] ids)
throws Exception {
if ((ids == null || ids.length == 0)) return;
LuceneIndexAdapter adapter = new LuceneIndexAdapter(context);
IndexWriter writer = null;
PreparedStatement st = null;
try {
if (adapter.getUsingSingleWriter()) {
writer = adapter.newWriter();
adapter.setAutoCommitSingleWriter(false);
}
ImsMetadataAdminDao dao = new ImsMetadataAdminDao(context);
String resourceTable = context.getCatalogConfiguration().getResourceTableName();
String resourceDataTable = context.getCatalogConfiguration().getResourceDataTableName();
StringSet delUuids = new StringSet();
MetadataAcl acl = new MetadataAcl(context);
boolean bCheckAcl = !acl.isPolicyUnrestricted();
StringBuilder sbIds = new StringBuilder();
for (String sUuid: ids) {
if (sbIds.length() > 0) sbIds.append(",");
sbIds.append("'").append(sUuid).append("'");
}
StringBuilder sb = new StringBuilder("SELECT");
sb.append(" ").append(resourceTable).append(".DOCUUID");
sb.append(",").append(resourceTable).append(".APPROVALSTATUS");
sb.append(",").append(resourceTable).append(".PROTOCOL_TYPE");
sb.append(",").append(resourceTable).append(".FINDABLE");
sb.append(",").append(resourceTable).append(".UPDATEDATE");
sb.append(",").append(resourceTable).append(".ACL");
sb.append(" FROM ").append(resourceTable);
sb.append(" WHERE DOCUUID IN (").append(sbIds.toString()).append(")");
String sql = sb.toString();
LOGGER.finest(sql);
ManagedConnection mc = context.getConnectionBroker().returnConnection("");
Connection con = mc.getJdbcConnection();
IClobMutator mutator = mc.getClobMutator();
st = con.prepareStatement(sql);
ResultSet rs = st.executeQuery();
if (Thread.interrupted()) return;
while (rs.next()) {
if (Thread.interrupted()) return;
String uuid = rs.getString(1);
String status = rs.getString(2);
String protocolType = Val.chkStr(rs.getString(3));
boolean findable = Val.chkBool(rs.getString(4),false);
boolean bIndexable = (status != null) &&
(status.equalsIgnoreCase("approved") || status.equalsIgnoreCase("reviewed"));
if (bIndexable && protocolType.length()>0 && !findable) {
bIndexable = false;
}
if (!bIndexable) {
delUuids.add(uuid);
} else {
Timestamp tsDbModified = rs.getTimestamp(5);
String sDbAcl = null;
if (bCheckAcl) {
sDbAcl = rs.getString(6);
}
try {
String sXml = Val.chkStr(dao.readXml(uuid));
if (sXml.length() > 0) {
MetadataDocument mdDoc = new MetadataDocument();
Schema schema = mdDoc.prepareForView(context,sXml);
adapter.publishDocument(uuid,tsDbModified,schema,sDbAcl);
}
} catch (SchemaException se) {
// don't allow the entire process to fail over one bad xml
String sMsg = "Error indexing document during the handling of a remote request";
sMsg += ", uuid="+uuid+"\n"+Val.chkStr(se.getMessage());
LOGGER.log(Level.WARNING,sMsg,se);
}
}
}
if (Thread.interrupted()) return;
if (delUuids.size() > 0) {
adapter.deleteDocuments(ids);
}
} finally {
try {if (st != null) st.close();} catch (Exception ef) {}
try {
if ((adapter != null) && (writer != null)) {
adapter.closeWriter(writer);
}
} catch (Exception ef) {
LOGGER.log(Level.WARNING,"IndexWriter failed to close.",ef);
}
}
}
/**
* Fully reads the characters from the request input stream.
* @param request the HTTP servlet request
* @return the characters read
* @throws IOException if an exception occurs
*/
protected String readInputCharacters(HttpServletRequest request)
throws IOException {
StringBuffer sb = new StringBuffer();
InputStream is = null;
InputStreamReader ir = null;
BufferedReader br = null;
try {
//if (request.getContentLength() > 0) {
char cbuf[] = new char[2048];
int n = 0;
int nLen = cbuf.length;
String sEncoding = request.getCharacterEncoding();
if ((sEncoding == null) || (sEncoding.trim().length() == 0)) {
sEncoding = "UTF-8";
}
is = request.getInputStream();
ir = new InputStreamReader(is,sEncoding);
br = new BufferedReader(ir);
while ((n = br.read(cbuf,0,nLen)) > 0) {
sb.append(cbuf,0,n);
}
//}
} finally {
try {if (br != null) br.close();} catch (Exception ef) {}
try {if (ir != null) ir.close();} catch (Exception ef) {}
try {if (is != null) is.close();} catch (Exception ef) {}
}
return sb.toString();
}
/**
* Writes characters to the response stream.
* @param response the servlet response
* @param content the content to write
* @param charset the response character encoding
* @param contentType the response content type
* @throws IOException if an IO exception occurs
*/
private void writeCharacterResponse(HttpServletResponse response,
String content,
String charset,
String contentType)
throws IOException {
PrintWriter writer = null;
try {
if (content.length() > 0) {
response.setCharacterEncoding(charset);
response.setContentType(contentType);
writer = response.getWriter();
writer.write(content);
writer.flush();
}
} finally {
try {
if (writer != null) {
writer.flush();
writer.close();
}
} catch (Exception ef) {
LOGGER.log(Level.SEVERE,"Error closing PrintWriter.",ef);
}
}
}
}