/**
* '$RCSfile$'
* Copyright: 2010 Regents of the University of California and the
* National Center for Ecological Analysis and Synthesis
*
* '$Author$'
* '$Date$'
* '$Revision$'
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.ucsb.nceas.metacat;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.XmlStreamReader;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.dataone.service.types.v1.AccessPolicy;
import org.dataone.service.types.v1.Checksum;
import org.dataone.service.types.v1.Event;
import org.dataone.service.types.v1.Identifier;
import org.dataone.service.types.v1.ObjectFormatIdentifier;
import org.dataone.service.types.v1.Session;
import org.dataone.service.types.v2.SystemMetadata;
import org.ecoinformatics.eml.EMLParser;
import au.com.bytecode.opencsv.CSVWriter;
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlException;
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlList;
import edu.ucsb.nceas.metacat.cart.CartManager;
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
import edu.ucsb.nceas.metacat.common.query.EnabledQueryEngines;
import edu.ucsb.nceas.metacat.common.resourcemap.ResourceMapNamespaces;
import edu.ucsb.nceas.metacat.database.DBConnection;
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
import edu.ucsb.nceas.metacat.dataone.SyncAccessPolicy;
import edu.ucsb.nceas.metacat.dataone.SystemMetadataFactory;
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
import edu.ucsb.nceas.metacat.dataquery.DataQuery;
import edu.ucsb.nceas.metacat.event.MetacatDocumentEvent;
import edu.ucsb.nceas.metacat.event.MetacatEventService;
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
import edu.ucsb.nceas.metacat.properties.PropertyService;
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
import edu.ucsb.nceas.metacat.service.SessionService;
import edu.ucsb.nceas.metacat.service.XMLSchemaService;
import edu.ucsb.nceas.metacat.shared.HandlerException;
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
import edu.ucsb.nceas.metacat.shared.ServiceException;
import edu.ucsb.nceas.metacat.spatial.SpatialHarvester;
import edu.ucsb.nceas.metacat.spatial.SpatialQuery;
import edu.ucsb.nceas.metacat.util.AuthUtil;
import edu.ucsb.nceas.metacat.util.DocumentUtil;
import edu.ucsb.nceas.metacat.util.MetacatUtil;
import edu.ucsb.nceas.metacat.util.RequestUtil;
import edu.ucsb.nceas.metacat.util.SessionData;
import edu.ucsb.nceas.metacat.util.SystemUtil;
import edu.ucsb.nceas.utilities.FileUtil;
import edu.ucsb.nceas.utilities.LSIDUtil;
import edu.ucsb.nceas.utilities.ParseLSIDException;
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
/**
* General entry point for the Metacat server which is called from various
* mechanisms such as the standard MetacatServlet class and the various web
* service servlets such as RestServlet class. All application logic should be
* encapsulated in this class, and the calling classes should only contain
* parameter marshaling and demarshaling code, delegating all else to this
* MetacatHandler instance.
* @author Matthew Jones
*/
public class MetacatHandler {
private static boolean _sitemapScheduled = false;
private static Logger logMetacat = Logger.getLogger(MetacatHandler.class);
// Constants -- these should be final in a servlet
private static final String PROLOG = "";
private static final String SUCCESS = "";
private static final String SUCCESSCLOSE = "";
private static final String ERROR = "";
private static final String ERRORCLOSE = "";
public static final String FGDCDOCTYPE = "metadata";
private Timer timer;
public MetacatHandler(Timer timer) {
this.timer = timer;
}
protected void handleDataquery(
Hashtable params,
HttpServletResponse response,
String sessionId) throws PropertyNotFoundException, IOException {
DataQuery dq = null;
if (sessionId != null) {
dq = new DataQuery(sessionId);
}
else {
dq = new DataQuery();
}
String dataqueryXML = (params.get("dataquery"))[0];
ResultSet rs = null;
try {
rs = dq.executeQuery(dataqueryXML);
} catch (Exception e) {
//probably need to do something here
e.printStackTrace();
return;
}
//process the result set
String qformat = "csv";
String[] temp = params.get("qformat");
if (temp != null && temp.length > 0) {
qformat = temp[0];
}
String fileName = "query-results." + qformat;
//get the results as csv file
if (qformat != null && qformat.equalsIgnoreCase("csv")) {
response.setContentType("text/csv");
//response.setContentType("application/csv");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
Writer writer = new OutputStreamWriter(response.getOutputStream());
CSVWriter csv = new CSVWriter(writer, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER);
try {
csv.writeAll(rs, true);
csv.flush();
response.flushBuffer();
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
return;
}
}
protected void handleEditCart(
Hashtable params,
HttpServletResponse response,
String sessionId) throws PropertyNotFoundException, IOException {
CartManager cm = null;
if (sessionId != null) {
cm = new CartManager(sessionId);
}
else {
cm = new CartManager();
}
String editOperation = (params.get("operation"))[0];
String[] docids = params.get("docid");
String[] field = params.get("field");
String[] path = params.get("path");
Map fields = null;
if (field != null && path != null) {
fields = new HashMap();
fields.put(field[0], path[0]);
}
//TODO handle attribute map (metadata fields)
cm.editCart(editOperation, docids, fields);
}
// ///////////////////////////// METACAT SPATIAL ///////////////////////////
/**
* handles all spatial queries -- these queries may include any of the
* queries supported by the WFS / WMS standards
*
* handleSQuery(out, params, response, username, groupnames, sess_id);
* @throws HandlerException
*/
protected void handleSpatialQuery(Writer out, Hashtable params,
HttpServletResponse response,
String username, String[] groupnames,
String sess_id) throws PropertyNotFoundException, HandlerException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
if ( !PropertyService.getProperty("spatial.runSpatialOption").equals("true") ) {
response.setContentType("text/html");
try {
out.write(" Metacat Spatial Option is turned off ");
out.close();
} catch (IOException e) {
throw new HandlerException(e.getMessage());
}
return;
}
/*
* Perform spatial query against spatial cache
*/
float _xmax = Float.valueOf( (params.get("xmax"))[0] ).floatValue();
float _ymax = Float.valueOf( (params.get("ymax"))[0] ).floatValue();
float _xmin = Float.valueOf( (params.get("xmin"))[0] ).floatValue();
float _ymin = Float.valueOf( (params.get("ymin"))[0] ).floatValue();
SpatialQuery sq = new SpatialQuery();
Vector docids = sq.filterByBbox( _xmin, _ymin, _xmax, _ymax );
// logMetacat.info(" --- Spatial Query completed. Passing on the SQuery
// handler");
// logMetacat.warn("\n\n ******* after spatial query, we've got " +
// docids.size() + " docids \n\n");
/*
* Create an array matching docids
*/
String [] docidArray = new String[docids.size()];
docids.toArray(docidArray);
/*
* Create squery string
*/
String squery = DocumentIdQuery.createDocidQuery( docidArray );
// logMetacat.info("-----------\n" + squery + "\n------------------");
String[] queryArray = new String[1];
queryArray[0] = squery;
params.put("query", queryArray);
/*
* Determine qformat
*/
String[] qformatArray = new String[1];
try {
String _skin = (params.get("skin"))[0];
qformatArray[0] = _skin;
} catch (java.lang.NullPointerException e) {
// should be "default" but keep this for backwards compatibility
// with knp site
logMetacat.warn("MetacatHandler.handleSpatialQuery - No SKIN specified for metacat actions=spatial_query... defaulting to 'knp' skin !\n");
qformatArray[0] = "knp";
}
params.put("qformat", qformatArray);
// change the action
String[] actionArray = new String[1];
actionArray[0] = "squery";
params.put("action", actionArray);
/*
* Pass the docids to the DBQuery contructor
*/
// This is a hack to get the empty result set to show...
// Otherwise dbquery sees no docidOverrides and does a full % percent
// query
if (docids.size() == 0)
docids.add("");
DBQuery queryobj = new DBQuery(docids);
queryobj.findDocuments(response, out, params, username, groupnames, sess_id);
}
// LOGIN & LOGOUT SECTION
/**
* Handle the login request. Create a new session object. Do user
* authentication through the session.
* @throws IOException
*/
public void handleLoginAction(Writer out, Hashtable params,
HttpServletRequest request, HttpServletResponse response) throws IOException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
AuthSession sess = null;
if(params.get("username") == null){
response.setContentType("text/xml");
out.write("");
out.write("");
out.write("Username not specified");
out.write("");
return;
}
// }
if(params.get("password") == null){
response.setContentType("text/xml");
out.write("");
out.write("");
out.write("Password not specified");
out.write("");
return;
}
String un = (params.get("username"))[0];
logMetacat.info("MetacatHandler.handleLoginAction - user " + un + " is trying to login");
String pw = (params.get("password"))[0];
String qformat = "xml";
if (params.get("qformat") != null) {
qformat = (params.get("qformat"))[0];
}
try {
sess = new AuthSession();
} catch (Exception e) {
String errorMsg = "MetacatServlet.handleLoginAction - Problem in MetacatServlet.handleLoginAction() authenicating session: "
+ e.getMessage();
logMetacat.error(errorMsg);
out.write(errorMsg);
e.printStackTrace(System.out);
return;
}
boolean isValid = sess.authenticate(request, un, pw);
//if it is authernticate is true, store the session
if (isValid) {
HttpSession session = sess.getSessions();
String id = session.getId();
logMetacat.debug("MetacatHandler.handleLoginAction - Store session id " + id
+ " which has username" + session.getAttribute("username")
+ " into hash in login method");
try {
//System.out.println("registering session with id " + id);
//System.out.println("username: " + (String) session.getAttribute("username"));
SessionService.getInstance().registerSession(id,
(String) session.getAttribute("username"),
(String[]) session.getAttribute("groupnames"),
(String) session.getAttribute("password"),
(String) session.getAttribute("name"));
} catch (ServiceException se) {
String errorMsg = "MetacatServlet.handleLoginAction - service problem registering session: "
+ se.getMessage();
logMetacat.error("MetacatHandler.handleLoginAction - " + errorMsg);
out.write(errorMsg);
se.printStackTrace(System.out);
return;
}
}
// format and transform the output
if (qformat.equals("xml")) {
response.setContentType("text/xml");
out.write(sess.getMessage());
} else {
try {
DBTransform trans = new DBTransform();
response.setContentType("text/html");
trans.transformXMLDocument(sess.getMessage(),
"-//NCEAS//login//EN", "-//W3C//HTML//EN", qformat,
out, null, null);
} catch (Exception e) {
logMetacat.error("MetacatHandler.handleLoginAction - General error"
+ e.getMessage());
e.printStackTrace(System.out);
}
}
}
/**
* Handle the logout request. Close the connection.
* @throws IOException
*/
public void handleLogoutAction(Writer out, Hashtable params,
HttpServletRequest request, HttpServletResponse response) throws IOException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
String qformat = "xml";
if(params.get("qformat") != null){
qformat = params.get("qformat")[0];
}
// close the connection
HttpSession sess = request.getSession(false);
logMetacat.info("MetacatHandler.handleLogoutAction - After get session in logout request");
if (sess != null) {
logMetacat.info("MetacatHandler.handleLogoutAction - The session id " + sess.getId()
+ " will be invalidate in logout action");
logMetacat.info("MetacatHandler.handleLogoutAction - The session contains user "
+ sess.getAttribute("username")
+ " will be invalidate in logout action");
sess.invalidate();
SessionService.getInstance().unRegisterSession(sess.getId());
}
// produce output
StringBuffer output = new StringBuffer();
output.append("");
output.append("");
output.append("User logged out");
output.append("");
//format and transform the output
if (qformat.equals("xml")) {
response.setContentType("text/xml");
out.write(output.toString());
} else {
try {
DBTransform trans = new DBTransform();
response.setContentType("text/html");
trans.transformXMLDocument(output.toString(),
"-//NCEAS//login//EN", "-//W3C//HTML//EN", qformat,
out, null, null);
} catch (Exception e) {
logMetacat.error(
"MetacatHandler.handleLogoutAction - General error: "
+ e.getMessage());
e.printStackTrace(System.out);
}
}
}
// END OF LOGIN & LOGOUT SECTION
// SQUERY & QUERY SECTION
/**
* Retreive the squery xml, execute it and display it
*
* @param out the output stream to the client
* @param params the Hashtable of parameters that should be included in the
* squery.
* @param response the response object linked to the client
* @param conn the database connection
*/
protected void handleSQuery(Writer out, Hashtable params,
HttpServletResponse response, String user, String[] groups,
String sessionid) throws PropertyNotFoundException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
long squeryWarnLimit = Long.parseLong(PropertyService.getProperty("database.squeryTimeWarnLimit"));
long startTime = System.currentTimeMillis();
DBQuery queryobj = new DBQuery();
queryobj.findDocuments(response, out, params, user, groups, sessionid);
long outPutTime = System.currentTimeMillis();
long runTime = outPutTime - startTime;
if (runTime > squeryWarnLimit) {
logMetacat.warn("MetacatHandler.handleSQuery - Long running squery. Total time: " + runTime +
" ms, squery: " + ((String[])params.get("query"))[0]);
}
logMetacat.debug("MetacatHandler.handleSQuery - squery: " + ((String[])params.get("query"))[0] +
" ran in " + runTime + " ms");
}
/**
* Create the xml query, execute it and display the results.
*
* @param out the output stream to the client
* @param params the Hashtable of parameters that should be included in the
* squery.
* @param response the response object linked to the client
* @throws IOException
* @throws UnsupportedEncodingException
*/
protected void handleQuery(Writer out, Hashtable params,
HttpServletResponse response, String user, String[] groups,
String sessionid) throws PropertyNotFoundException, UnsupportedEncodingException, IOException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
long queryWarnLimit = Long.parseLong(PropertyService.getProperty("database.queryTimeWarnLimit"));
//create the query and run it
String xmlquery = DBQuery.createSQuery(params);
String[] queryArray = new String[1];
queryArray[0] = xmlquery;
params.put("query", queryArray);
long startTime = System.currentTimeMillis();
DBQuery queryobj = new DBQuery();
queryobj.findDocuments(response, out, params, user, groups, sessionid);
long outPutTime = System.currentTimeMillis();
long runTime = outPutTime -startTime;
if (runTime > queryWarnLimit) {
logMetacat.warn("MetacatHandler.handleQuery - Long running squery. Total time: " + runTime +
" ms, squery: " + ((String[])params.get("query"))[0]);
}
logMetacat.debug("MetacatHandler.handleQuery - query: " + ((String[])params.get("query"))[0] +
" ran in " + runTime + " ms");
}
// END OF SQUERY & QUERY SECTION
//Export section
/**
* Handle the "export" request of data package from Metacat in zip format
*
* @param params the Hashtable of HTTP request parameters
* @param response the HTTP response object linked to the client
* @param user the username sent the request
* @param groups the user's groupnames
*/
protected void handleExportAction(Hashtable params,
HttpServletResponse response,
String user, String[] groups, String passWord) {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
// Output stream
ServletOutputStream out = null;
// Zip output stream
ZipOutputStream zOut = null;
DBQuery queryObj = null;
String[] docs = new String[10];
String docId = "";
try {
// read the params
if (params.containsKey("docid")) {
docs = params.get("docid");
}
// Create a DBuery to handle export
queryObj = new DBQuery();
String qformat = null;
if (params.containsKey("qformat")) {
qformat = params.get("qformat")[0];
queryObj.setQformat(qformat);
}
// Get the docid
docId = docs[0];
// Make sure the client specify docid
if (docId == null || docId.equals("")) {
response.setContentType("text/xml"); //MIME type
// Get a printwriter
PrintWriter pw = response.getWriter();
// Send back message
pw.println("");
pw.println("");
pw.println("You didn't specify requested docid");
pw.println("");
// Close printwriter
pw.close();
return;
}
// Get output stream
response.setContentType("application/zip"); //MIME type
response.setHeader("Content-Disposition",
"attachment; filename="
+ docId + ".zip"); // Set the name of the zip file
out = response.getOutputStream();
zOut = new ZipOutputStream(out);
zOut = queryObj
.getZippedPackage(docId, out, user, groups, passWord);
zOut.finish(); //terminate the zip file
zOut.close(); //close the zip stream
} catch (Exception e) {
try {
response.setContentType("text/xml"); //MIME type
// Send error message back
if (out != null) {
PrintWriter pw = new PrintWriter(out);
pw.println("");
pw.println("");
pw.println(e.getMessage());
pw.println("");
// Close printwriter
pw.close();
// Close output stream
out.close();
}
// Close zip output stream
if (zOut != null) {
zOut.close();
}
} catch (IOException ioe) {
logMetacat.error("MetacatHandler.handleExportAction - Problem with the servlet output: "
+ ioe.getMessage());
e.printStackTrace(System.out);
}
logMetacat.error("MetacatHandler.handleExportAction - General error: "
+ e.getMessage());
e.printStackTrace(System.out);
}
}
/**
* In eml2 document, the xml can have inline data and data was stripped off
* and store in file system. This action can be used to read inline data
* only
*
* @param params the Hashtable of HTTP request parameters
* @param response the HTTP response object linked to the client
* @param user the username sent the request
* @param groups the user's groupnames
*/
protected void handleReadInlineDataAction(Hashtable params,
HttpServletRequest request, HttpServletResponse response,
String user, String passWord, String[] groups) {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
String[] docs = new String[10];
String inlineDataId = null;
String docId = "";
ServletOutputStream out = null;
try {
// read the params
if (params.containsKey("inlinedataid")) {
docs = params.get("inlinedataid");
}
// Get the docid
inlineDataId = docs[0];
// Make sure the client specify docid
if (inlineDataId == null || inlineDataId.equals("")) {
throw new Exception("You didn't specify requested inlinedataid"); }
// check for permission, use full docid with revision
docId = DocumentUtil.getDocIdFromInlineDataID(inlineDataId);
PermissionController controller = new PermissionController(docId);
// check top level read permission
if (!controller.hasPermission(user, groups,
AccessControlInterface.READSTRING)) {
throw new Exception("User " + user
+ " doesn't have permission " + " to read document "
+ docId);
} else {
//check data access level
try {
Hashtable unReadableInlineDataList =
PermissionController.getUnReadableInlineDataIdList(docId, user, groups);
if (unReadableInlineDataList.containsValue(inlineDataId)) {
throw new Exception("User " + user
+ " doesn't have permission " + " to read inlinedata "
+ inlineDataId);
}//if
}//try
catch (Exception e) {
throw e;
}//catch
}//else
// Get output stream
out = response.getOutputStream();
// read the inline data from the file
String inlinePath = PropertyService.getProperty("application.inlinedatafilepath");
File lineData = new File(inlinePath, inlineDataId);
FileInputStream input = new FileInputStream(lineData);
byte[] buffer = new byte[4 * 1024];
int bytes = input.read(buffer);
while (bytes != -1) {
out.write(buffer, 0, bytes);
bytes = input.read(buffer);
}
out.close();
EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), user,
inlineDataId, "readinlinedata");
} catch (Exception e) {
try {
PrintWriter pw = null;
// Send error message back
if (out != null) {
pw = new PrintWriter(out);
} else {
pw = response.getWriter();
}
pw.println("");
pw.println("");
pw.println(e.getMessage());
pw.println("");
// Close printwriter
pw.close();
// Close output stream if out is not null
if (out != null) {
out.close();
}
} catch (IOException ioe) {
logMetacat.error("MetacatHandler.handleReadInlineDataAction - Problem with the servlet output: "
+ ioe.getMessage());
e.printStackTrace(System.out);
}
logMetacat.error("MetacatHandler.handleReadInlineDataAction - General error: "
+ e.getMessage());
e.printStackTrace(System.out);
}
}
/**
* Handle the "read" request of metadata/data files from Metacat or any
* files from Internet; transformed metadata XML document into HTML
* presentation if requested; zip files when more than one were requested.
*
* @param params the Hashtable of HTTP request parameters
* @param request the HTTP request object linked to the client
* @param response the HTTP response object linked to the client
* @param user the username sent the request
* @param groups the user's groupnames
*/
public void handleReadAction(Hashtable params, HttpServletRequest request,
HttpServletResponse response, String user, String passWord,
String[] groups) {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
ServletOutputStream out = null;
ZipOutputStream zout = null;
PrintWriter pw = null;
boolean zip = false;
boolean withInlineData = true;
try {
String[] docs = new String[0];
String docid = "";
String qformat = "";
// read the params
if (params.containsKey("docid")) {
docs = params.get("docid");
}
if (params.containsKey("qformat")) {
qformat = params.get("qformat")[0];
}
// the param for only metadata (eml)
// we don't support read a eml document without inline data now.
/*if (params.containsKey("inlinedata")) {
String inlineData = ((String[]) params.get("inlinedata"))[0];
if (inlineData.equalsIgnoreCase("false")) {
withInlineData = false;
}
}*/
// handle special case where the PID was given
if (params.containsKey("pid")) {
docs = params.get("pid");
for (int i = 0; i < docs.length; i++) {
String pid = docs[i];
// look up the pid if we have it
String localId = IdentifierManager.getInstance().getLocalId(pid);
docs[i] = localId;
}
// put docid in parms for downstream methods to use
params.put("docid", docs);
}
if ((docs.length > 1) || qformat.equals("zip")) {
zip = true;
out = response.getOutputStream();
response.setContentType("application/zip"); //MIME type
zout = new ZipOutputStream(out);
}
// go through the list of docs to read
for (int i = 0; i < docs.length; i++) {
String providedFileName = null;
if (params.containsKey(docs[i])) {
providedFileName = params.get(docs[i])[0];
}
try {
URL murl = new URL(docs[i]);
Hashtable murlQueryStr = MetacatUtil.parseQuery(
murl.getQuery());
// case docid="http://.../?docid=aaa"
// or docid="metacat://.../?docid=bbb"
if (murlQueryStr.containsKey("docid")) {
// get only docid, eliminate the rest
docid = murlQueryStr.get("docid");
if (zip) {
addDocToZip(request, docid, providedFileName, zout, user, groups);
} else {
readFromMetacat(request.getRemoteAddr(), request.getHeader("User-Agent"), response, response.getOutputStream(), docid, qformat,
user, groups, withInlineData, params);
}
// case docid="http://.../filename"
} else {
docid = docs[i];
if (zip) {
addDocToZip(request, docid, providedFileName, zout, user, groups);
} else {
readFromURLConnection(response, docid);
}
}
} catch (MalformedURLException mue) {
docid = docs[i];
if (zip) {
addDocToZip(request, docid, providedFileName, zout, user, groups);
} else {
if (out == null) {
out = response.getOutputStream();
}
readFromMetacat(request.getRemoteAddr(), request.getHeader("User-Agent"), response, out, docid, qformat,
user, groups, withInlineData, params);
}
}
}
if (zip) {
zout.finish(); //terminate the zip file
zout.close(); //close the zip stream
}
} catch (McdbDocNotFoundException notFoundE) {
// To handle doc not found exception
// the docid which didn't be found
String notFoundDocId = notFoundE.getUnfoundDocId();
String notFoundRevision = notFoundE.getUnfoundRevision();
logMetacat.warn("MetacatHandler.handleReadAction - Missed id: " + notFoundDocId);
logMetacat.warn("MetacatHandler.handleReadAction - Missed rev: " + notFoundRevision);
try {
// read docid from remote server
readFromRemoteMetaCat(response, notFoundDocId,
notFoundRevision, user, passWord, out, zip, zout);
// Close zout outputstream
if (zout != null) {
zout.close();
}
// close output stream
if (out != null) {
out.close();
}
} catch (Exception exc) {
logMetacat.error("MetacatHandler.handleReadAction - General error: "
+ exc.getMessage());
exc.printStackTrace(System.out);
try {
if (out != null) {
response.setContentType("text/xml");
// Send back error message by printWriter
pw = new PrintWriter(out);
pw.println("");
pw.println("");
pw.println(notFoundE.getMessage());
pw.println("");
pw.close();
out.close();
} else {
response.setContentType("text/xml"); //MIME type
// Send back error message if out = null
if (pw == null) {
// If pw is null, open the respnose
pw = response.getWriter();
}
pw.println("");
pw.println("");
pw.println(notFoundE.getMessage());
pw.println("");
pw.close();
}
// close zout
if (zout != null) {
zout.close();
}
} catch (IOException ie) {
logMetacat.error("MetacatHandler.handleReadAction - Problem with the servlet output: "
+ ie.getMessage());
ie.printStackTrace(System.out);
}
}
} catch (Exception e) {
try {
if (out != null) {
response.setContentType("text/xml"); //MIME type
pw = new PrintWriter(out);
pw.println("");
pw.println("");
pw.println(e.getMessage());
pw.println("");
pw.close();
out.close();
} else {
response.setContentType("text/xml"); //MIME type
// Send back error message if out = null
if (pw == null) {
pw = response.getWriter();
}
pw.println("");
pw.println("");
pw.println(e.getMessage());
pw.println("");
pw.close();
}
// Close zip output stream
if (zout != null) {
zout.close();
}
} catch (Exception e2) {
logMetacat.error("MetacatHandler.handleReadAction - " +
"Problem with the servlet output: "+
e2.getMessage());
e2.printStackTrace(System.out);
}
logMetacat.error("MetacatHandler.handleReadAction - General error: "
+ e.getMessage());
e.printStackTrace(System.out);
}
}
/**
*
* @return
*/
public MetacatResultSet query(String metacatUrl, Hashtableparams,
String username, String[] groups, String sessionid)
throws Exception
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// use UTF-8 encoding for DB query
Writer out = new OutputStreamWriter(baos, MetaCatServlet.DEFAULT_ENCODING);
handleQuery(out, params, null, username, groups, sessionid);
out.flush();
baos.flush();
//baos.close();
//System.out.println("result from query: " + baos.toString());
MetacatResultSet rs = new MetacatResultSet(baos.toString(MetaCatServlet.DEFAULT_ENCODING));
return rs;
}
/**
* set the access permissions on the document specified
*/
public void setAccess(String metacatUrl, String username, String docid, String principal,
String permission, String permissionType, String permissionOrder)
throws Exception
{
Hashtable params = new Hashtable();
params.put("principal", new String[] {principal});
params.put("permission", new String[] {permission});
params.put("permType", new String[] {permissionType});
params.put("permOrder", new String[] {permissionOrder});
params.put("docid", new String[]{docid});
//System.out.println("permission in MetacatHandler.setAccess: " +
// params.get("permission")[0]);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(baos);
handleSetAccessAction(out, params, username, null, null);
String resp = baos.toString();
//System.out.println("response from MetacatHandler.setAccess: " + resp);
}
/**
* Read a document from metacat and return the InputStream. The XML or
* data document should be on disk, but if not, read from the metacat database.
*
* @param docid - the metacat docid to read
* @param username - the DN of the principal attempting the read
* @param groups - the list of groups the DN belongs to as a String array
* @return objectStream - the document as an InputStream
* @throws InsufficientKarmaException
* @throws ParseLSIDException
* @throws PropertyNotFoundException
* @throws McdbException
* @throws SQLException
* @throws ClassNotFoundException
* @throws IOException
*/
public static InputStream read(String docid) throws ParseLSIDException,
PropertyNotFoundException, McdbException, SQLException,
ClassNotFoundException, IOException {
logMetacat.debug("MetacatHandler.read() called.");
InputStream inputStream = null;
// be sure we have a local ID from an LSID
if (docid.startsWith("urn:")) {
try {
docid = LSIDUtil.getDocId(docid, true);
} catch (ParseLSIDException ple) {
logMetacat.debug("There was a problem parsing the LSID. The "
+ "error message was: " + ple.getMessage());
throw ple;
}
}
// accomodate old clients that send docids without revision numbers
docid = DocumentUtil.appendRev(docid);
DocumentImpl doc = new DocumentImpl(docid, false);
// deal with data or metadata cases
if (doc.getRootNodeID() == 0) {
// this is a data file
// get the path to the file to read
try {
String filepath = PropertyService.getProperty("application.datafilepath");
// ensure it is a directory path
if (!(filepath.endsWith("/"))) {
filepath += "/";
}
String filename = filepath + docid;
inputStream = readFromFilesystem(filename);
} catch (PropertyNotFoundException pnf) {
logMetacat.debug("There was a problem finding the "
+ "application.datafilepath property. The error "
+ "message was: " + pnf.getMessage());
throw pnf;
} // end try()
} else {
// this is an metadata document
// Get the xml (will try disk then DB)
try {
// force the InputStream to be returned
OutputStream nout = null;
inputStream = doc.toXml(nout, null, null, true);
} catch (McdbException e) {
// report the error
logMetacat.error(
"MetacatHandler.readFromMetacat() "
+ "- could not read document " + docid + ": "
+ e.getMessage(), e);
}
}
return inputStream;
}
/**
* Read a file from Metacat's configured file system data directory.
*
* @param filename The full path file name of the file to read
*
* @return fileInputStream The file to read as a FileInputStream
*/
private static FileInputStream readFromFilesystem(String filename)
throws FileNotFoundException {
logMetacat.debug("MetacatHandler.readFromFilesystem() called.");
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(filename);
} catch ( FileNotFoundException fnfe ) {
logMetacat.debug("There was an error reading the file " +
filename + ". The error was: " +
fnfe.getMessage());
throw fnfe;
}
return fileInputStream;
}
/*
* Delete a document in metacat based on the docid.
*
* @param out - the print writer used to send output
* @param response - the HTTP servlet response to be returned
* @param docid - the internal docid as a String
* @param user - the username of the principal doing the delete
* @param groups - the groups list of the principal doing the delete
*
* @throws AccessionNumberException
* @throws McdbDocNotFoundException
* @throws InsufficientKarmaException
* @throws SQLException
* @throws Exception
*/
private void deleteFromMetacat(PrintWriter out, HttpServletRequest request,
HttpServletResponse response, String docid, String user, String[] groups)
throws McdbDocNotFoundException {
// Delete a document from the database based on the docid
try {
DocumentImpl.delete(docid, user, groups, null, false); // null: don't notify server
EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"),
user, docid, "delete");
response.setContentType("text/xml");
out.println(this.PROLOG);
out.println(this.SUCCESS);
out.println("Document deleted.");
out.println(this.SUCCESSCLOSE);
logMetacat.info("MetacatHandler.handleDeleteAction - " +
"Document deleted.");
try {
// Delete from spatial cache if runningSpatialOption
if ( PropertyService.getProperty("spatial.runSpatialOption").equals("true") ) {
SpatialHarvester sh = new SpatialHarvester();
sh.addToDeleteQue( DocumentUtil.getSmartDocId( docid ) );
sh.destroy();
}
} catch ( PropertyNotFoundException pnfe ) {
logMetacat.error("MetacatHandler.deleteFromMetacat() - " +
"Couldn't find spatial.runSpatialOption property during " +
"document deletion.");
}
} catch (AccessionNumberException ane) {
response.setContentType("text/xml");
out.println(this.PROLOG);
out.println(this.ERROR);
//out.println("Error deleting document!!!");
out.println(ane.getMessage());
out.println(this.ERRORCLOSE);
logMetacat.error("MetacatHandler.deleteFromMetacat() - " +
"Document could not be deleted: "
+ ane.getMessage());
ane.printStackTrace(System.out);
} catch ( SQLException sqle ) {
response.setContentType("text/xml");
out.println(this.PROLOG);
out.println(this.ERROR);
//out.println("Error deleting document!!!");
out.println(sqle.getMessage());
out.println(this.ERRORCLOSE);
logMetacat.error("MetacatHandler.deleteFromMetacat() - " +
"Document could not be deleted: "
+ sqle.getMessage());
sqle.printStackTrace(System.out);
} catch ( McdbDocNotFoundException dnfe ) {
throw dnfe;
} catch ( InsufficientKarmaException ike ) {
response.setContentType("text/xml");
out.println(this.PROLOG);
out.println(this.ERROR);
//out.println("Error deleting document!!!");
out.println(ike.getMessage());
out.println(this.ERRORCLOSE);
logMetacat.error("MetacatHandler.deleteFromMetacat() - " +
"Document could not be deleted: "
+ ike.getMessage());
ike.printStackTrace(System.out);
} catch ( Exception e ) {
response.setContentType("text/xml");
out.println(this.PROLOG);
out.println(this.ERROR);
//out.println("Error deleting document!!!");
out.println(e.getMessage());
out.println(this.ERRORCLOSE);
logMetacat.error("MetacatHandler.deleteFromMetacat() - " +
"Document could not be deleted: "
+ e.getMessage());
e.printStackTrace(System.out);
}
}
/** read metadata or data from Metacat
* @param userAgent
* @throws PropertyNotFoundException
* @throws ParseLSIDException
* @throws InsufficientKarmaException
*/
public void readFromMetacat(String ipAddress, String userAgent,
HttpServletResponse response, OutputStream out, String docid, String qformat,
String user, String[] groups, boolean withInlineData,
Hashtable params) throws ClassNotFoundException,
IOException, SQLException, McdbException, PropertyNotFoundException,
ParseLSIDException, InsufficientKarmaException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
try {
if (docid.startsWith("urn:")) {
docid = LSIDUtil.getDocId(docid, true);
}
// here is hack for handle docid=john.10(in order to tell mike.jim.10.1
// mike.jim.10, we require to provide entire docid with rev). But
// some old client they only provide docid without rev, so we need
// to handle this suituation. First we will check how many
// seperator here, if only one, we will append the rev in xml_documents
// to the id.
docid = DocumentUtil.appendRev(docid);
DocumentImpl doc = new DocumentImpl(docid, false);
//check the permission for read
if (!DocumentImpl.hasReadPermission(user, groups, docid)) {
InsufficientKarmaException e =
new InsufficientKarmaException("User " + user
+ " does not have permission"
+ " to read the document with the docid " + docid);
throw e;
}
if (doc.getRootNodeID() == 0) {
// this is data file, so find the path on disk for the file
String filepath = PropertyService.getProperty("application.datafilepath");
if (!filepath.endsWith("/")) {
filepath += "/";
}
String filename = filepath + docid;
FileInputStream fin = null;
fin = new FileInputStream(filename);
if (response != null) {
// MIME type
//String contentType = servletContext.getMimeType(filename);
String contentType = (new MimetypesFileTypeMap()).getContentType(filename);
if (contentType == null) {
ContentTypeProvider provider = new ContentTypeProvider(
docid);
contentType = provider.getContentType();
logMetacat.info("MetacatHandler.readFromMetacat - Final contenttype is: "
+ contentType);
}
response.setContentType(contentType);
// Set the output filename on the response
String outputname = generateOutputName(docid, params, doc);
response.setHeader("Content-Disposition",
"attachment; filename=\"" + outputname + "\"");
}
// Write the data to the output stream
try {
byte[] buf = new byte[4 * 1024]; // 4K buffer
int b = fin.read(buf);
while (b != -1) {
out.write(buf, 0, b);
b = fin.read(buf);
}
fin.close();
} finally {
IOUtils.closeQuietly(fin);
}
} else {
// this is metadata doc
if (qformat.equals("xml") || qformat.equals("")) {
// if equals "", that means no qformat is specified. hence
// by default the document should be returned in xml format
// set content type first
if (response != null) {
response.setContentType("text/xml"); //MIME type
response.setHeader("Content-Disposition",
"attachment; filename=" + docid + ".xml");
}
// Try to get the metadata file from disk. If it isn't
// found, create it from the db and write it to disk then.
try {
doc.toXml(out, user, groups, withInlineData);
} catch (McdbException e) {
// any exceptions in reading the xml from disc, and we go back to the
// old way of creating the xml directly.
logMetacat.error("MetacatHandler.readFromMetacat - " +
"could not read from document file " +
docid +
": " +
e.getMessage());
e.printStackTrace(System.out);
doc.toXmlFromDb(out, user, groups, withInlineData);
}
} else {
// TODO MCD, this should read from disk as well?
//*** This is a metadata doc, to be returned in a skin/custom format.
//*** Add param to indicate if public has read access or not.
logMetacat.debug("User: \n" + user);
if (!user.equals("public")) {
if (DocumentImpl.hasReadPermission("public", null, docid))
params.put("publicRead", new String[] {"true"});
else
params.put("publicRead", new String[] {"false"});
}
if(doc.getDoctype() != null && doc.getDoctype().equals(FGDCDOCTYPE)) {
//for fgdc doctype, we need to pass parameter enableFGDCediting
PermissionController controller = new PermissionController(docid);
if(controller.hasPermission(user, groups, AccessControlInterface.WRITESTRING)) {
params.put("enableFGDCediting", new String[] {"true"});
} else {
params.put("enableFGDCediting", new String[] {"false"});
}
}
if (response != null) {
response.setContentType("text/html"); //MIME type
}
// detect actual encoding
String docString = doc.toString(user, groups, withInlineData);
XmlStreamReader xsr =
new XmlStreamReader(new ByteArrayInputStream(doc.getBytes()));
String encoding = xsr.getEncoding();
Writer w = null;
if (encoding != null) {
w = new OutputStreamWriter(out, encoding);
} else {
w = new OutputStreamWriter(out);
}
// Look up the document type
String doctype = doc.getDoctype();
// Transform the document to the new doctype
DBTransform dbt = new DBTransform();
dbt.transformXMLDocument(
docString,
doctype, "-//W3C//HTML//EN",
qformat,
w,
params,
null);
}
}
EventLog.getInstance().log(ipAddress, userAgent, user, docid, "read");
} catch (PropertyNotFoundException except) {
throw except;
}
}
/**
* Create a filename to be used for naming a downloaded document
* @param docid the identifier of the document to be named
* @param params the parameters of the request
* @param doc the DocumentImpl of the document to be named
* @return String containing a name for the download
*/
private String generateOutputName(String docid,
Hashtable params, DocumentImpl doc) {
SystemMetadata sysMeta = null;
String guid = null;
int rev = -1;
String fileName = null;
// First, if SystemMetadata.fileName is present, use it
try {
rev = Integer.valueOf(DocumentUtil.getRevisionStringFromString(docid)).intValue();
docid = DocumentUtil.getDocIdFromAccessionNumber(docid);
if (rev > 0 ) {
guid = IdentifierManager.getInstance().getGUID(docid, rev);
if ( guid != null ) {
sysMeta = IdentifierManager.getInstance().getSystemMetadata(guid);
if ( sysMeta != null ) {
fileName = sysMeta.getFileName();
}
}
}
} catch (McdbDocNotFoundException e) {
logMetacat.debug("Couldn't find the given docid: " + e.getMessage());
}
if (fileName != null ) {
return fileName;
}
// Otherwise, generate a name
String outputname = null;
// check for the existence of a metadatadocid parameter,
// if this is sent, then send a filename which contains both
// the metadata docid and the data docid, so the link with
// metadata is explicitly encoded in the filename.
String metadatadocid = null;
Vector nameparts = new Vector();
if(params.containsKey("metadatadocid")) {
metadatadocid = params.get("metadatadocid")[0];
}
if (metadatadocid != null && !metadatadocid.equals("")) {
nameparts.add(metadatadocid);
}
// we'll always have the docid, include it in the name
String doctype = doc.getDoctype();
// TODO: fix this to lookup the associated FGDC metadata document,
// and grab the doctype tag for it. These should be set to something
// consistent, not 'metadata' as it stands...
//if (!doctype.equals("metadata")) {
// nameparts.add(docid);
//}
nameparts.add(docid);
// Set the name of the data file to the entity name plus docid,
// or if that is unavailable, use the docid alone
String docname = doc.getDocname();
if (docname != null && !docname.equals("")) {
nameparts.add(docname);
}
// combine the name elements with a dash, using a 'join' equivalent
String delimiter = "-";
Iterator iter = nameparts.iterator();
StringBuffer buffer = new StringBuffer(iter.next());
while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
outputname = buffer.toString();
return outputname;
}
/**
* read data from URLConnection
*/
private void readFromURLConnection(HttpServletResponse response,
String docid) throws IOException, MalformedURLException {
ServletOutputStream out = response.getOutputStream();
//String contentType = servletContext.getMimeType(docid); //MIME type
String contentType = (new MimetypesFileTypeMap()).getContentType(docid);
if (contentType == null) {
if (docid.endsWith(".xml")) {
contentType = "text/xml";
} else if (docid.endsWith(".css")) {
contentType = "text/css";
} else if (docid.endsWith(".dtd")) {
contentType = "text/plain";
} else if (docid.endsWith(".xsd")) {
contentType = "text/xml";
} else if (docid.endsWith("/")) {
contentType = "text/html";
} else {
File f = new File(docid);
if (f.isDirectory()) {
contentType = "text/html";
} else {
contentType = "application/octet-stream";
}
}
}
response.setContentType(contentType);
// if we decide to use "application/octet-stream" for all data returns
// response.setContentType("application/octet-stream");
// this is http url
URL url = new URL(docid);
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(url.openStream());
byte[] buf = new byte[4 * 1024]; // 4K buffer
int b = bis.read(buf);
while (b != -1) {
out.write(buf, 0, b);
b = bis.read(buf);
}
} finally {
if (bis != null) bis.close();
}
}
/**
* read file/doc and write to ZipOutputStream
*
* @param docid
* @param zout
* @param user
* @param groups
* @throws ClassNotFoundException
* @throws IOException
* @throws SQLException
* @throws McdbException
* @throws Exception
*/
private void addDocToZip(HttpServletRequest request, String docid, String providedFileName,
ZipOutputStream zout, String user, String[] groups) throws
ClassNotFoundException, IOException, SQLException, McdbException,
Exception {
byte[] bytestring = null;
ZipEntry zentry = null;
try {
URL url = new URL(docid);
// this http url; read from URLConnection; add to zip
//use provided file name if we have one
if (providedFileName != null && providedFileName.length() > 1) {
zentry = new ZipEntry(providedFileName);
}
else {
zentry = new ZipEntry(docid);
}
zout.putNextEntry(zentry);
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(url.openStream());
byte[] buf = new byte[4 * 1024]; // 4K buffer
int b = bis.read(buf);
while (b != -1) {
zout.write(buf, 0, b);
b = bis.read(buf);
}
} finally {
if (bis != null) bis.close();
}
zout.closeEntry();
} catch (MalformedURLException mue) {
// this is metacat doc (data file or metadata doc)
try {
DocumentImpl doc = new DocumentImpl(docid, false);
//check the permission for read
if (!DocumentImpl.hasReadPermission(user, groups, docid)) {
Exception e = new Exception("User " + user
+ " does not have "
+ "permission to read the document with the docid "
+ docid);
throw e;
}
if (doc.getRootNodeID() == 0) {
// this is data file; add file to zip
String filepath = PropertyService.getProperty("application.datafilepath");
if (!filepath.endsWith("/")) {
filepath += "/";
}
String filename = filepath + docid;
FileInputStream fin = null;
fin = new FileInputStream(filename);
try {
//use provided file name if we have one
if (providedFileName != null && providedFileName.length() > 1) {
zentry = new ZipEntry(providedFileName);
}
else {
zentry = new ZipEntry(docid);
}
zout.putNextEntry(zentry);
byte[] buf = new byte[4 * 1024]; // 4K buffer
int b = fin.read(buf);
while (b != -1) {
zout.write(buf, 0, b);
b = fin.read(buf);
}
fin.close();
} finally {
IOUtils.closeQuietly(fin);
}
zout.closeEntry();
} else {
// this is metadata doc; add doc to zip
bytestring = doc.getBytes();
//use provided file name if given
if (providedFileName != null && providedFileName.length() > 1) {
zentry = new ZipEntry(providedFileName);
}
else {
zentry = new ZipEntry(docid + ".xml");
}
zentry.setSize(bytestring.length);
zout.putNextEntry(zentry);
zout.write(bytestring, 0, bytestring.length);
zout.closeEntry();
}
EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), user,
docid, "read");
} catch (Exception except) {
throw except;
}
}
}
/**
* If metacat couldn't find a data file or document locally, it will read
* this docid from its home server. This is for the replication feature
*/
private void readFromRemoteMetaCat(HttpServletResponse response,
String docid, String rev, String user, String password,
ServletOutputStream out, boolean zip, ZipOutputStream zout)
throws Exception {
// Create a object of RemoteDocument, "" is for zipEntryPath
RemoteDocument remoteDoc = new RemoteDocument(docid, rev, user,
password, "");
String docType = remoteDoc.getDocType();
// Only read data file
if (docType.equals("BIN")) {
// If it is zip format
if (zip) {
remoteDoc.readDocumentFromRemoteServerByZip(zout);
} else {
if (out == null) {
out = response.getOutputStream();
}
response.setContentType("application/octet-stream");
remoteDoc.readDocumentFromRemoteServer(out);
}
} else {
throw new Exception("Docid: " + docid + "." + rev
+ " couldn't find");
}
}
/**
* Handle the database putdocument request and write an XML document to the
* database connection
* @param userAgent
* @param generateSystemMetadata
*/
public String handleInsertOrUpdateAction(String ipAddress, String userAgent,
HttpServletResponse response, PrintWriter out, Hashtable params,
String user, String[] groups, boolean generateSystemMetadata, boolean writeAccessRules, byte[] xmlBytes, String formatId, Checksum checksum) {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
DBConnection dbConn = null;
int serialNumber = -1;
String output = "";
String qformat = null;
if(params.containsKey("qformat"))
{
qformat = params.get("qformat")[0];
}
if(params.get("docid") == null){
String msg = this.PROLOG +
this.ERROR +
"Docid not specified" +
this.ERRORCLOSE;
if(out != null)
{
out.println(msg);
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - Docid not specified");
}
return msg;
}
try {
if (!AuthUtil.canInsertOrUpdate(user, groups)) {
String msg = this.PROLOG +
this.ERROR +
"User '" +
user +
"' is not allowed to insert and update" +
this.ERRORCLOSE;
if(out != null)
{
out.println(msg);
}
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - " +
"User '" +
user +
"' not allowed to insert and update");
return msg;
}
} catch (MetacatUtilException ue) {
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - " +
"Could not determine if user could insert or update: " +
ue.getMessage(), ue);
String msg = this.PROLOG +
this.ERROR +
"MetacatHandler.handleInsertOrUpdateAction - " +
"Could not determine if user could insert or update: " +
ue.getMessage() +
this.ERRORCLOSE;
if(out != null)
{
out.println(msg);
}
return msg;
}
try {
// Get the document indicated
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - params: " +
params.toString());
String[] doctext = params.get("doctext");
String pub = null;
if (params.containsKey("public")) {
pub = params.get("public")[0];
}
StringReader dtd = null;
if (params.containsKey("dtdtext")) {
String[] dtdtext = params.get("dtdtext");
try {
if (!dtdtext[0].equals("")) {
dtd = new StringReader(dtdtext[0]);
}
} catch (NullPointerException npe) {
}
}
if(doctext == null){
String msg = this.PROLOG +
this.ERROR +
"Document text not submitted." +
this.ERRORCLOSE;
if(out != null)
{
out.println(msg);
}
// TODO: this should really throw an exception
return msg;
}
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - " +
"the xml document in metacat servlet (before parsing):\n" +
doctext[0]);
StringReader xmlReader = new StringReader(doctext[0]);
boolean validate = false;
DocumentImplWrapper documentWrapper = null;
String namespace = null;
String schemaLocation = null;
try {
// look inside XML Document for
// in order to decide whether to use validation parser
validate = needDTDValidation(xmlReader);
if (validate) {
// set a dtd base validation parser
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - the xml object will be validate by a dtd");
String rule = DocumentImpl.DTD;
documentWrapper = new DocumentImplWrapper(rule, validate, writeAccessRules);
} else {
XMLSchemaService.getInstance().doRefresh();
namespace = XMLSchemaService.findDocumentNamespace(xmlReader);
if (namespace != null) {
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - the xml object will be validated by a schema which has a target namespace: "+namespace);
schemaLocation = XMLSchemaService.getInstance().findNamespaceAndSchemaLocalLocation(formatId, namespace);
if (namespace.compareTo(DocumentImpl.EML2_0_0NAMESPACE) == 0
|| namespace.compareTo(
DocumentImpl.EML2_0_1NAMESPACE) == 0) {
// set eml2 base validation parser
String rule = DocumentImpl.EML200;
// using emlparser to check id validation
@SuppressWarnings("unused")
EMLParser parser = new EMLParser(doctext[0]);
documentWrapper = new DocumentImplWrapper(rule, true, writeAccessRules);
} else if (
namespace.compareTo(DocumentImpl.EML2_1_0NAMESPACE) == 0
|| namespace.compareTo(DocumentImpl.EML2_1_1NAMESPACE) == 0) {
// set eml2 base validation parser
String rule = DocumentImpl.EML210;
// using emlparser to check id validation
@SuppressWarnings("unused")
EMLParser parser = new EMLParser(doctext[0]);
documentWrapper = new DocumentImplWrapper(rule, true, writeAccessRules);
} else {
if(!XMLSchemaService.isNamespaceRegistered(namespace)) {
throw new Exception("The namespace "+namespace+" used in the xml object hasn't been registered in the Metacat. Metacat can't validate the object and rejected it. Please contact the operator of the Metacat for regsitering the namespace.");
}
// set schema base validation parser
String rule = DocumentImpl.SCHEMA;
documentWrapper = new DocumentImplWrapper(rule, true, writeAccessRules);
}
} else {
xmlReader = new StringReader(doctext[0]);
String noNamespaceSchemaLocationAttr = XMLSchemaService.findNoNamespaceSchemaLocationAttr(xmlReader);
if(noNamespaceSchemaLocationAttr != null) {
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - the xml object will be validated by a schema which deoe NOT have a target namespace.");
schemaLocation = XMLSchemaService.getInstance().findNoNamespaceSchemaLocalLocation(formatId, noNamespaceSchemaLocationAttr);
String rule = DocumentImpl.NONAMESPACESCHEMA;
documentWrapper = new DocumentImplWrapper(rule, true, writeAccessRules);
} else {
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - the xml object will NOT be validated.");
documentWrapper = new DocumentImplWrapper("", false, writeAccessRules);
}
}
}
String[] action = params.get("action");
String[] docid = params.get("docid");
String newdocid = null;
String doAction = null;
if (action[0].equals("insert") || action[0].equals("insertmultipart")) {
doAction = "INSERT";
} else if (action[0].equals("update")) {
doAction = "UPDATE";
}
try {
// get a connection from the pool
dbConn = DBConnectionPool
.getDBConnection("Metacathandler.handleInsertOrUpdateAction");
serialNumber = dbConn.getCheckOutSerialNumber();
// write the document to the database and disk
String accNumber = docid[0];
logMetacat.debug("MetacatHandler.handleInsertOrUpdateAction - " +
doAction + " " + accNumber + "...");
Identifier identifier = new Identifier();
identifier.setValue(accNumber);
if(!D1NodeService.isValidIdentifier(identifier)) {
String error = "The docid "+accNumber +" is not valid since it is null or contians the white space(s).";
logMetacat.warn("MetacatHandler.handleInsertOrUpdateAction - " +error);
throw new Exception(error);
}
/*if (accNumber == null || accNumber.equals("")) {
logMetacat.warn("MetacatHandler.handleInsertOrUpdateAction - " +
"writing with null acnumber");
newdocid = documentWrapper.write(dbConn, doctext[0], pub, dtd,
doAction, null, user, groups);
EventLog.getInstance().log(ipAddress, userAgent, user, "", action[0]);
} else {*/
newdocid = documentWrapper.write(dbConn, doctext[0], pub, dtd,
doAction, accNumber, user, groups, xmlBytes, schemaLocation, checksum);
EventLog.getInstance().log(ipAddress, userAgent, user, accNumber, action[0]);
//}
// alert listeners of this event
MetacatDocumentEvent mde = new MetacatDocumentEvent();
mde.setDocid(accNumber);
mde.setDoctype(namespace);
mde.setAction(doAction);
mde.setUser(user);
mde.setGroups(groups);
MetacatEventService.getInstance().notifyMetacatEventObservers(mde);
// if it was called from Metacat API, we want to generate system metadata for it
if ( generateSystemMetadata ) {
SystemMetadata sysMeta = null;
// it's possible that system metadata exists although
// older clients don't support it. Try updates first.
try {
// get the docid parts
String docidWithoutRev = DocumentUtil.getSmartDocId(newdocid);
int rev = IdentifierManager.getInstance().getLatestRevForLocalId(newdocid);
String guid = IdentifierManager.getInstance().getGUID(docidWithoutRev, rev);
sysMeta = IdentifierManager.getInstance().getSystemMetadata(guid);
// TODO: need to update? we just looked it up
//IdentifierManager.getInstance().updateSystemMetadata(sysMeta);
} catch ( McdbDocNotFoundException mnfe) {
// handle inserts
try {
// create the system metadata. During the creatation, the data file in the eml may need to be reindexed.
boolean reindexDataObject = true;
sysMeta = SystemMetadataFactory.createSystemMetadata(reindexDataObject, newdocid, true, false);
// save it to the map
HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
// submit for indexing
MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
// [re]index the resource map now that everything is saved
// see: https://projects.ecoinformatics.org/ecoinfo/issues/6520
Identifier potentialOreIdentifier = new Identifier();
potentialOreIdentifier.setValue(SystemMetadataFactory.RESOURCE_MAP_PREFIX + sysMeta.getIdentifier().getValue());
SystemMetadata oreSystemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(potentialOreIdentifier);
if (oreSystemMetadata != null) {
MetacatSolrIndex.getInstance().submit(oreSystemMetadata.getIdentifier(), oreSystemMetadata, null, true);
}
} catch ( McdbDocNotFoundException dnfe ) {
logMetacat.warn(
"There was a problem finding the localId " +
newdocid + "The error was: " + dnfe.getMessage());
throw dnfe;
} catch ( AccessionNumberException ane ) {
logMetacat.error(
"There was a problem creating the accession number " +
"for " + newdocid + ". The error was: " + ane.getMessage());
throw ane;
} // end try()
}
} // end if()
} finally {
// Return db connection
DBConnectionPool.returnDBConnection(dbConn, serialNumber);
}
// set content type and other response header fields first
//response.setContentType("text/xml");
output += this.PROLOG;
output += this.SUCCESS;
output += "" + newdocid + "";
output += this.SUCCESSCLOSE;
} catch (NullPointerException npe) {
//response.setContentType("text/xml");
output += this.PROLOG;
output += this.ERROR;
output += npe.getMessage();
output += this.ERRORCLOSE;
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - " +
"Null pointer error when writing eml " +
"document to the database: " +
npe.getMessage());
npe.printStackTrace();
}
} catch (Exception e) {
//response.setContentType("text/xml");
output += this.PROLOG;
output += this.ERROR;
output += e.getMessage();
output += this.ERRORCLOSE;
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - " +
"General error when writing the xml object " +
"document to the database: " +
e.getMessage(), e);
e.printStackTrace();
}
if (qformat == null || qformat.equals("xml")) {
if(response != null && out != null)
{
response.setContentType("text/xml");
out.println(output);
}
return output;
} else {
try {
DBTransform trans = new DBTransform();
response.setContentType("text/html");
trans.transformXMLDocument(output,
"message", "-//W3C//HTML//EN", qformat,
out, null, null);
return output;
} catch (Exception e) {
logMetacat.error("MetacatHandler.handleInsertOrUpdateAction - " +
"General error: " +
e.getMessage());
e.printStackTrace(System.out);
}
}
return output;
}
/**
* Parse XML Document to look for in
* order to decide whether to use validation parser
*/
private static boolean needDTDValidation(StringReader xmlreader)
throws IOException {
Logger logMetacat = Logger.getLogger(MetacatHandler.class);
StringBuffer cbuff = new StringBuffer();
java.util.Stack st = new java.util.Stack();
boolean validate = false;
boolean commented = false;
int c;
int inx;
// read from the stream until find the keywords
while ((st.empty() || st.size() < 4) && ((c = xmlreader.read()) != -1)) {
cbuff.append((char) c);
if ((inx = cbuff.toString().indexOf("