From 2b5af0f084a7c3a55a73a57442029120de09b0d3 Mon Sep 17 00:00:00 2001
From: Brian Caruso
Date: Mon, 15 Jul 2013 14:12:42 -0400
Subject: [PATCH 1/3] Adding streaming support to
MultipartHttpServletRequest.java
---
.../FileUploadServletRequest.java | 9 ---
.../MultipartHttpServletRequest.java | 61 ++++++++++++++++---
2 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java
index ca564ea3c..8f795df1c 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java
@@ -2,21 +2,12 @@
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.Principal;
-import java.util.Enumeration;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java
index 2df1daa0c..b78da5318 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java
@@ -29,29 +29,60 @@ import org.apache.commons.logging.LogFactory;
* any parameter-related requests. File-related information will also be held
* here, to answer file-related requests.
*/
-class MultipartHttpServletRequest extends FileUploadServletRequest {
+public class MultipartHttpServletRequest extends FileUploadServletRequest {
private static final Log log = LogFactory
.getLog(MultipartHttpServletRequest.class);
private static final String[] EMPTY_ARRAY = new String[0];
- private final Map> parameters;
- private final Map> files;
+ private Map> parameters;
+ private Map> files;
private FileUploadException fileUploadException;
+ private boolean storeFilesToTempDir = false;
+ private int maxFileSize = 0;
+ private File tempDir = null;
+
+ /**
+ * Parse the multipart request. Store the info about the request parameters.
+ * Don't store the uploaded files to a temporary directory to allow streaming.
+ *
+ * Only use this constructor if you plan to consume the FileItems using streaming
+ * to deal with inputs of very large sizes.
+ *
+ * In all other case you should use the maxFileSize constructor to deal with
+ * the size of the uploaded file in a safe way.
+ */
+ public MultipartHttpServletRequest(HttpServletRequest request)
+ throws IOException{
+ super(request);
+ storeFilesToTempDir = false;
+
+ }
+
/**
* Parse the multipart request. Store the info about the request parameters
- * and the uploaded files.
+ * and the uploaded files to a temporary directory.
+ *
+ * This offers a simple way to deal with uploaded files by having a size
+ * limit. This limit may be rather large if desired. Many megabytes for example.
*/
public MultipartHttpServletRequest(HttpServletRequest request,
int maxFileSize) throws IOException {
super(request);
+ storeFilesToTempDir = true;
+ this.maxFileSize = maxFileSize;
+ this.tempDir = figureTemporaryDirectory(request);
+ }
+ private void setup(HttpServletRequest request){
Map> parameters = new HashMap>();
Map> files = new HashMap>();
- File tempDir = figureTemporaryDirectory(request);
- ServletFileUpload upload = createUploadHandler(maxFileSize, tempDir);
+ ServletFileUpload upload = createUploadHandler();
+
+ //File tempDir = figureTemporaryDirectory(request);
+ //ServletFileUpload upload = createUploadHandler(maxFileSize, tempDir);
parseQueryString(request.getQueryString(), parameters);
@@ -75,14 +106,16 @@ class MultipartHttpServletRequest extends FileUploadServletRequest {
fileUploadException = e;
request.setAttribute(
FileUploadServletRequest.FILE_UPLOAD_EXCEPTION, e);
- }
+ } catch (UnsupportedEncodingException e) {
+ log.error("could not convert to UTF-8",e);
+ }
this.parameters = Collections.unmodifiableMap(parameters);
log.debug("Parameters are: " + this.parameters);
this.files = Collections.unmodifiableMap(files);
log.debug("Files are: " + this.files);
request.setAttribute(FILE_ITEM_MAP, this.files);
- }
+ }
/**
* Pull any parameters out of the URL.
@@ -131,6 +164,18 @@ class MultipartHttpServletRequest extends FileUploadServletRequest {
"javax.servlet.context.tempdir");
}
+
+ /**
+ * Create an upload handler based on this.storeFilesToTempDir.
+ */
+ private ServletFileUpload createUploadHandler(){
+ if( storeFilesToTempDir ){
+ return createUploadHandler( this.maxFileSize, this.tempDir );
+ }else{
+ return new ServletFileUpload();
+ }
+ }
+
/**
* Create an upload handler that will throw an exception if the file is too
* large.
From 7905b80df41f5ded688df572ed6ee0f697ecb84b Mon Sep 17 00:00:00 2001
From: Brian Caruso
Date: Mon, 15 Jul 2013 14:14:15 -0400
Subject: [PATCH 2/3] Adding initial search web service for updating uris.
VIVO-98
---
.../controller/SearchServiceController.java | 92 ++++++++++
.../search/controller/UpdateUrisInIndex.java | 172 ++++++++++++++++++
.../webapp/search/indexing/IndexBuilder.java | 18 +-
.../controller/UpdateUrisInIndexTest.java | 66 +++++++
webapp/web/WEB-INF/web.xml | 9 +
.../body/search/searchService-help.ftl | 18 ++
6 files changed, 372 insertions(+), 3 deletions(-)
create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndexTest.java
create mode 100644 webapp/web/templates/freemarker/body/search/searchService-help.ftl
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
new file mode 100644
index 000000000..22116f3e6
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
@@ -0,0 +1,92 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.search.controller;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
+import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
+import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.MultipartHttpServletRequest;
+import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
+
+/**
+ * Accepts requests to update a set of URIs in the search index.
+ */
+@SuppressWarnings("serial")
+public class SearchServiceController extends FreemarkerHttpServlet {
+ private static final Log log = LogFactory.getLog(SearchServiceController.class);
+
+ @Override
+ protected Actions requiredActions(VitroRequest vreq) {
+ return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
+ }
+
+ /**
+ * Handle the different actions. If not specified, the default action is to
+ * show the help page.
+ */
+ @Override
+ protected ResponseValues processRequest(VitroRequest req) {
+ try {
+ // Works by side effect: parse the multi-part request and stash FileItems in request
+ new MultipartHttpServletRequest( req );
+
+ //figure out what action to perform
+ String pathInfo = req.getPathInfo();
+
+ if( pathInfo == null || pathInfo.trim().isEmpty() || "/".equals(pathInfo.trim()) ){
+ return doHelpForm(req);
+ }
+
+ pathInfo = pathInfo.substring(1); //get rid of leading slash
+
+ if (VERBS.UPDATE_URIS_IN_SEARCH.verb.equals( pathInfo )) {
+ return doUpdateUrisInSearch(req);
+ } else {
+ return doHelpForm(req);
+ }
+ } catch (Exception e) {
+ return new ExceptionResponseValues(e);
+ }
+ }
+
+
+ public ResponseValues doUpdateUrisInSearch(HttpServletRequest req )
+ throws IOException, ServletException {
+
+ IndexBuilder builder = IndexBuilder.getBuilder(getServletContext());
+ if( builder == null )
+ throw new ServletException( "Could not get search index builder from context. Check smoke test");
+
+ new UpdateUrisInIndex().doUpdateUris( req, builder);
+
+ TemplateResponseValues trv = new TemplateResponseValues( "" );
+ return trv;
+ }
+
+
+ public ResponseValues doHelpForm(HttpServletRequest req){
+ return new TemplateResponseValues( "searchService-help.ftl");
+ }
+
+ public enum VERBS{
+ UPDATE_URIS_IN_SEARCH("updateUrisInSearch");
+
+ public final String verb;
+ VERBS(String verb){
+ this.verb = verb;
+ }
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
new file mode 100644
index 000000000..9f8689021
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
@@ -0,0 +1,172 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.search.controller;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
+
+/**
+ * Class that performs the update of the uris in the search index
+ * for the SearchService.
+ */
+
+public class UpdateUrisInIndex {
+ private static final Log log = LogFactory.getLog(UpdateUrisInIndex.class);
+
+ /**
+ * Web service for update in search index of a list of URIs.
+ * @throws IOException
+ */
+ protected void doUpdateUris(HttpServletRequest req, IndexBuilder builder)
+ throws ServletException, IOException{
+
+
+ boolean isMultipart = ServletFileUpload.isMultipartContent(req);
+ if( ! isMultipart )
+ throw new ServletException("Expected Multipart Content");
+
+
+ String charEncoding = getEncoding(req);
+ try{
+ int count = 0;
+ ServletFileUpload upload = new ServletFileUpload();
+ FileItemIterator iter = upload.getItemIterator(req);
+ while( iter.hasNext()){
+ FileItemStream item = iter.next();
+ String name = item.getFieldName();
+ InputStream stream = item.openStream();
+ try{
+ count = count + addToSearchQueue(builder, new InputStreamReader(stream, charEncoding));
+ }finally{
+ stream.close();
+ builder.doUpdateIndex();
+ }
+ }
+ }catch (FileUploadException fex){
+ throw new ServletException("Could not upload file to SearchServiceController", fex);
+ }
+ }
+
+ private String getEncoding(HttpServletRequest req){
+ String enc = req.getCharacterEncoding();
+ if( enc == null || enc.isEmpty() ){
+ log.debug("No encoding on POST request, That is acceptable.");
+ enc = "UTF-8";
+ }else if( enc.length() > 30){
+ log.debug("Ignoring odd encoding of '" + enc + "'");
+ enc = "UTF-8";
+ }else{
+ log.debug("Encoding set on POST request: " + enc);
+ }
+ log.debug("Reading POSTed URIs with encoding " + enc);
+ return enc;
+ }
+
+ private int addToSearchQueue( IndexBuilder builder, Reader in )
+ throws IOException{
+ int addedUriCount = 0;
+
+ Iterator uris = new UrisFromInputIterator( in );
+ while(uris.hasNext()){
+ String uri = uris.next();
+ log.debug("Request to index uri '" + uri + "'");
+ builder.addToChanged( uri );
+ addedUriCount++;
+ }
+
+ return addedUriCount;
+ }
+
+
+ public static class UrisFromInputIterator implements Iterator {
+ BufferedReader reader;
+ Iterator uris;
+
+ public UrisFromInputIterator(Reader in ){
+ this.reader = new BufferedReader(in);
+ }
+
+ public void remove(){ throw new UnsupportedOperationException() ; }
+
+ public boolean hasNext(){
+ if( uris != null && uris.hasNext() ){
+ return true;
+ }else{
+ try {
+ return getFromBuffer();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+
+ public String next(){
+ return uris.next();
+ }
+
+ /** Returns true if there are uris to get.
+ * @throws IOException */
+ private boolean getFromBuffer() throws IOException{
+ uris = null;
+
+ while( uris == null || !uris.hasNext() ){
+ String chunk = reader.readLine();
+ if( chunk == null ){ //at end of input
+ break;
+ } else if( chunk.trim().isEmpty() ){
+ continue;
+ }else{
+ uris = lineToUris(chunk).iterator();
+ if( uris.hasNext() ){
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+
+
+ private static List removeNullAndEmpty(List in ){
+ ArrayList out = new ArrayList();
+ for( String s : in ){
+ if( s != null && !s.trim().isEmpty() ){
+ out.add(s);
+ }
+ }
+ return out;
+ }
+
+ protected static List lineToUris(String line){
+ List parts = removeNullAndEmpty( Arrays.asList(commaAndWhitespace.split( line ) ));
+ return parts;
+ }
+
+ //split uris on whitespace and commas
+ private static final Pattern commaAndWhitespace = Pattern.compile("[,\\s]");
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/IndexBuilder.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/IndexBuilder.java
index add4210a2..ab8e4750d 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/IndexBuilder.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/IndexBuilder.java
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.query.QueryParseException;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
@@ -130,12 +131,22 @@ public class IndexBuilder extends VitroBackgroundThread {
* your changes with a call to doUpdateIndex().
*/
public void addToChanged(Statement stmt) {
- log.debug("call to addToChanged()");
+ log.debug("call to addToChanged(Statement)");
synchronized(changedStmts){
changedStmts.add(stmt);
}
}
+ /**
+ * Convenience method to add a URI to the change queue.
+ */
+ public void addToChanged(String uri){
+ addToChanged(ResourceFactory.createStatement(
+ ResourceFactory.createResource(uri),
+ ResourceFactory.createProperty("http://ex.com/f"),
+ ResourceFactory.createPlainLiteral("added by IndexBuilder.addToChanged(uri)")));
+ }
+
/**
* This method will cause the IndexBuilder to completely rebuild
* the index.
@@ -244,7 +255,8 @@ public class IndexBuilder extends VitroBackgroundThread {
for( StatementToURIsToUpdate stu : stmtToURIsToIndexFunctions ) {
stu.startIndexing();
}
-
+
+ //keep uris unique by using a HashSet
Collection urisToUpdate = new HashSet();
for( Statement stmt : getAndClearChangedStmts() ){
for( StatementToURIsToUpdate stu : stmtToURIsToIndexFunctions ){
@@ -256,7 +268,7 @@ public class IndexBuilder extends VitroBackgroundThread {
for( StatementToURIsToUpdate stu : stmtToURIsToIndexFunctions ) {
stu.endIndxing();
}
-
+
return urisToUpdate;
}
diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndexTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndexTest.java
new file mode 100644
index 000000000..662c68e95
--- /dev/null
+++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndexTest.java
@@ -0,0 +1,66 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.search.controller;
+
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Accepts requests to update a set of URIs in the search index.
+ */
+public class UpdateUrisInIndexTest {
+
+ @Test
+ public void lineToUrisTest(){
+ Assert.assertEquals(Arrays.asList("uri1"), UpdateUrisInIndex.lineToUris( "uri1"));
+ Assert.assertEquals(Arrays.asList("uri1", "uri2"), UpdateUrisInIndex.lineToUris( "uri1,uri2"));
+
+ Assert.assertEquals(Arrays.asList("uri1"), UpdateUrisInIndex.lineToUris( "uri1\n"));
+ Assert.assertEquals(Arrays.asList("uri1","uri2"), UpdateUrisInIndex.lineToUris( "uri1\nuri2"));
+
+ Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( "" ));
+ Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( "," ));
+ Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( " , " ));
+ }
+
+
+ @Test
+ public void UrisFromInputIteratorTest(){
+ doUrisFromInputIterator("",0);
+ doUrisFromInputIterator(" ",0);
+ doUrisFromInputIterator(" , ",0);
+ doUrisFromInputIterator("\n",0);
+ doUrisFromInputIterator("\n\n\n",0);
+ doUrisFromInputIterator("http://bogus.com/n234",1);
+ doUrisFromInputIterator("http://bogus.com/n234\nhttp://bogus.com/n442",2);
+ doUrisFromInputIterator("http://bogus.com/n234, http://bogus.com/n442",2);
+ doUrisFromInputIterator("http://bogus.com/n234,\nhttp://bogus.com/n442\n",2);
+
+ doUrisFromInputIterator("http://bogus.com/n234\n",1);
+ doUrisFromInputIterator("\nhttp://bogus.com/n234",1);
+ doUrisFromInputIterator("\nhttp://bogus.com/n234\n",1);
+
+ }
+
+ public void doUrisFromInputIterator(String input, int expectedUris){
+ Iterator it = new UpdateUrisInIndex.UrisFromInputIterator( new StringReader(input) );
+ int count = 0;
+ while( it.hasNext()){
+ String uri = it.next();
+ if( uri == null)
+ Assert.fail("UrisFromInputIterator should not return null strings \n " +
+ "Null string for uri #" + count + " for input '" + input + "'");
+ if( uri.isEmpty())
+ Assert.fail("UrisFromInputIterator should not return empty strings \n " +
+ "Empty string for uri #" + count + " for input '" + input + "'");
+ count++;
+ }
+ Assert.assertEquals("Incorrect number of URIs from input '" + input + "'", expectedUris, count);
+ }
+
+}
diff --git a/webapp/web/WEB-INF/web.xml b/webapp/web/WEB-INF/web.xml
index 10d3944f4..2b246330a 100644
--- a/webapp/web/WEB-INF/web.xml
+++ b/webapp/web/WEB-INF/web.xml
@@ -1151,6 +1151,15 @@
/admin/getObjectClasses
+
+ SearchServiceController
+ edu.cornell.mannlib.vitro.webapp.search.controller.SearchServiceController
+
+
+ SearchServiceController
+ /searchService/*
+
+
GadgetController
diff --git a/webapp/web/templates/freemarker/body/search/searchService-help.ftl b/webapp/web/templates/freemarker/body/search/searchService-help.ftl
new file mode 100644
index 000000000..6101e000b
--- /dev/null
+++ b/webapp/web/templates/freemarker/body/search/searchService-help.ftl
@@ -0,0 +1,18 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+
+Search Web Service
+
+Add information here about how to use the Search Web Service.
+
+*** FOLLOWING FORM IS TOTALLY MESSED UP WITH A HARDCODED URL ***
+
+
+
From fb46e64725b1a86b93f4cc52232b04801d62eb11 Mon Sep 17 00:00:00 2001
From: Brian Caruso
Date: Tue, 16 Jul 2013 12:28:24 -0400
Subject: [PATCH 3/3] Adding login via email/password parameters
---
.../controller/SearchServiceController.java | 67 ++++++++++++++++++-
.../search/controller/UpdateUrisInIndex.java | 58 ++++++++++------
.../body/search/searchService-help.ftl | 40 ++++++++---
3 files changed, 134 insertions(+), 31 deletions(-)
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
index 22116f3e6..61c23afc2 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java
@@ -10,9 +10,17 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import edu.cornell.mannlib.vitro.webapp.auth.identifier.ActiveIdentifierBundleFactories;
+import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
+import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
+import edu.cornell.mannlib.vitro.webapp.auth.policy.ServletPolicyList;
+import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyIface;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
+import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.BasicAuthenticator;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
@@ -27,11 +35,64 @@ import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
public class SearchServiceController extends FreemarkerHttpServlet {
private static final Log log = LogFactory.getLog(SearchServiceController.class);
+ /**
+ * Attempt to check if there is a email and password in the request parameters.
+ * If there is not, fall back on the normal usage pattern of requestedAction().
+ * If there is, then try to authenticate and authorize the
+ * userAccount associated with the email.
+ */
@Override
protected Actions requiredActions(VitroRequest vreq) {
- return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
+ try{
+ // Works by side effect: parse the multi-part request and stash FileItems in request
+ new MultipartHttpServletRequest( vreq );
+
+ //first figure out if the password and email login to an account with a password
+ String pw = vreq.getParameter("password");
+ String email = vreq.getParameter("email");
+
+ log.debug(String.format("email: '%s' password: '%s' ",email,pw));
+
+ if( pw == null || email == null || pw.isEmpty() || email.isEmpty()){
+ return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
+ }
+
+ Authenticator basicAuth = new BasicAuthenticator(vreq);
+ UserAccount user = basicAuth.getAccountForInternalAuth( email );
+ log.debug("userAccount is " + user==null?"null":user.getUri() );
+
+ if( ! basicAuth.isCurrentPassword( user, pw ) ){
+ log.debug(String.format("UNAUTHORIZED, password not accepted for %s, account URI: %s",
+ user.getEmailAddress(), user.getUri()));
+ return Actions.UNAUTHORIZED;
+ }else{
+ log.debug(String.format("password accepted for %s, account URI: %s",
+ user.getEmailAddress(), user.getUri() ));
+ }
+
+ //then figure out if that account can manage the search index.
+ IdentifierBundle ids =
+ ActiveIdentifierBundleFactories.getUserIdentifierBundle(vreq,user);
+ PolicyIface policy = ServletPolicyList.getPolicies(vreq);
+ boolean canManageSearchIndex =
+ PolicyHelper.isAuthorizedForActions( ids, policy,
+ SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS );
+ if( canManageSearchIndex ){
+ return Actions.AUTHORIZED;
+ }else{
+ log.debug(String.format("userAccount is unauthorized to" +
+ " manage the search index.",user.getUri()));
+ return Actions.UNAUTHORIZED;
+ }
+
+ }catch(Exception ex){
+ log.error("Error while attempting to log in " +
+ "to SearchServiceController: " + ex.getMessage());
+ return Actions.UNAUTHORIZED;
+ }
}
+
/**
* Handle the different actions. If not specified, the default action is to
* show the help page.
@@ -39,8 +100,6 @@ public class SearchServiceController extends FreemarkerHttpServlet {
@Override
protected ResponseValues processRequest(VitroRequest req) {
try {
- // Works by side effect: parse the multi-part request and stash FileItems in request
- new MultipartHttpServletRequest( req );
//figure out what action to perform
String pathInfo = req.getPathInfo();
@@ -62,6 +121,8 @@ public class SearchServiceController extends FreemarkerHttpServlet {
}
+ /**
+ * Process requests to the web service to update a list of URIs in the search index. */
public ResponseValues doUpdateUrisInSearch(HttpServletRequest req )
throws IOException, ServletException {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
index 9f8689021..94998759d 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java
@@ -41,33 +41,39 @@ public class UpdateUrisInIndex {
protected void doUpdateUris(HttpServletRequest req, IndexBuilder builder)
throws ServletException, IOException{
-
- boolean isMultipart = ServletFileUpload.isMultipartContent(req);
- if( ! isMultipart )
+ if( ! ServletFileUpload.isMultipartContent(req) )
throw new ServletException("Expected Multipart Content");
-
- String charEncoding = getEncoding(req);
+ String enc = getEncoding(req);
try{
- int count = 0;
+ //loop over the fileds and add any URIs to the IndexBuilder queue
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iter = upload.getItemIterator(req);
while( iter.hasNext()){
FileItemStream item = iter.next();
String name = item.getFieldName();
+ if( "email".equals(name) || "password".equals(name) )
+ continue; //skip the password and email fields
+
InputStream stream = item.openStream();
try{
- count = count + addToSearchQueue(builder, new InputStreamReader(stream, charEncoding));
+ addToSearchQueue(builder, stream, enc);
}finally{
stream.close();
- builder.doUpdateIndex();
}
}
}catch (FileUploadException fex){
throw new ServletException("Could not upload file to SearchServiceController", fex);
+ }finally{
+ builder.doUpdateIndex();
}
}
+ /**
+ * Get the encoding of the request, default to UTF-8
+ * since that is in the vitro install instructions
+ * to put on the connector.
+ */
private String getEncoding(HttpServletRequest req){
String enc = req.getCharacterEncoding();
if( enc == null || enc.isEmpty() ){
@@ -83,22 +89,26 @@ public class UpdateUrisInIndex {
return enc;
}
- private int addToSearchQueue( IndexBuilder builder, Reader in )
+ /**
+ * Adds URIs from Reader to search queue.
+ */
+ private void addToSearchQueue( IndexBuilder builder, InputStream stream , String charEncoding )
throws IOException{
- int addedUriCount = 0;
- Iterator uris = new UrisFromInputIterator( in );
+ Iterator uris =
+ new UrisFromInputIterator( new InputStreamReader(stream, charEncoding) );
+
while(uris.hasNext()){
String uri = uris.next();
log.debug("Request to index uri '" + uri + "'");
builder.addToChanged( uri );
- addedUriCount++;
}
-
- return addedUriCount;
}
+ /**
+ * Iterator for URIs in a reader to make top level methods simpler.
+ */
public static class UrisFromInputIterator implements Iterator {
BufferedReader reader;
Iterator uris;
@@ -148,11 +158,16 @@ public class UpdateUrisInIndex {
return false;
}
}
-
-
-
- private static List removeNullAndEmpty(List in ){
+
+ /**
+ * Removes null and empty elements from in.
+ * Returned list will not be null.
+ */
+ private static List removeNullAndEmpty(List in ){
ArrayList out = new ArrayList();
+ if( in == null )
+ return out;
+
for( String s : in ){
if( s != null && !s.trim().isEmpty() ){
out.add(s);
@@ -161,12 +176,17 @@ public class UpdateUrisInIndex {
return out;
}
+ /**
+ * Parses a line to a list of URIs.
+ * Retruned list will not be null.
+ * No elements in returned list will be empty or null.
+ */
protected static List lineToUris(String line){
List parts = removeNullAndEmpty( Arrays.asList(commaAndWhitespace.split( line ) ));
return parts;
}
- //split uris on whitespace and commas
+ /** Pattern to split URIs on whitespace and commas. */
private static final Pattern commaAndWhitespace = Pattern.compile("[,\\s]");
}
diff --git a/webapp/web/templates/freemarker/body/search/searchService-help.ftl b/webapp/web/templates/freemarker/body/search/searchService-help.ftl
index 6101e000b..d1e61061c 100644
--- a/webapp/web/templates/freemarker/body/search/searchService-help.ftl
+++ b/webapp/web/templates/freemarker/body/search/searchService-help.ftl
@@ -1,18 +1,40 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
-Search Web Service
+Search Web Service
-Add information here about how to use the Search Web Service.
+<#if msg?has_content >
+ Message:
+ ${msg}
+#if>
-*** FOLLOWING FORM IS TOTALLY MESSED UP WITH A HARDCODED URL ***
+Update Search Index for URIs
-
+
+The request parameters email and password allow the form to be submitted
+for a user account. Only internal accounts are supported, external authentication
+is not supported.
+
+Example form for Update Search Index for URIs
+The following form will post to the Update URIs in search service.
+
+
-
+
\ No newline at end of file