Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving Data Distribution API into the Vitro codebase #484

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,31 @@

<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>enforce-java</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>11</version>
<message>You are running an incompatible version of Java. Minimum required version is 11.</message>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<runOrder>alphabetical</runOrder>
</configuration>
Expand Down Expand Up @@ -91,6 +113,11 @@
<version>1.15.1-SNAPSHOT</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
Expand All @@ -104,6 +131,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.15.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
///* $This file is distributed under the terms of the license in /doc/license.txt$ */

package edu.cornell.library.scholars.webapp.controller.api;

import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY;
import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.ActionFailedException;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.DataDistributorException;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.MissingParametersException;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.NoSuchActionException;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.NotAuthorizedException;
import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributorContextImpl;
import edu.cornell.mannlib.vitro.webapp.controller.api.VitroApiServlet;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoaderException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.rdf.model.Model;

/**
* Find a distributor description for the requested action. Create an instance
* of that distributor. Write its data to the HTTP response.
*/
@WebServlet(name = "DistributeDataApi", urlPatterns = { "/api/dataRequest/*" })
public class DistributeDataApiController extends VitroApiServlet {
private static final Log log = LogFactory.getLog(DistributeDataApiController.class);

private static final String DISTRIBUTOR_FOR_SPECIFIED_ACTION = ""
+ "PREFIX : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> \n" + "SELECT ?distributor \n" //
+ "WHERE { \n" //
+ " ?distributor :actionName ?action . \n" //
+ "} \n";

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Model model = ModelAccess.on(req).getOntModel(DISPLAY);

String uri = findDistributorForAction(req, model);
DataDistributor instance = instantiateDistributor(req, uri, model);
runIt(req, resp, instance);
} catch (NoSuchActionException e) {
do400BadRequest(e.getMessage(), resp);
} catch (MissingParametersException e) {
do400BadRequest(e.getMessage(), resp);
} catch (NotAuthorizedException e) {
do403Forbidden(resp);
} catch (Exception e) {
do500InternalServerError(e.getMessage(), e, resp);
}
}

private String findDistributorForAction(HttpServletRequest req, Model model) throws NoSuchActionException {
String action = req.getPathInfo();
if (action == null || action.isEmpty()) {
throw new NoSuchActionException("'action' path was not provided.");
}
if (action.startsWith("/")) {
action = action.substring(1);
}

return findDistributorUri(model, action);
}

public static String findDistributorUri(Model model, String action) throws NoSuchActionException {
List<String> uris = createSelectQueryContext(model, DISTRIBUTOR_FOR_SPECIFIED_ACTION)
.bindVariableToPlainLiteral("action", action).execute().toStringFields("distributor").flatten();
Collections.sort(uris);
log.debug("Found URIs for action '" + action + "': " + uris);

if (uris.isEmpty()) {
throw new NoSuchActionException("Did not find a DataDistributor for '" + action + "'");
}
if (uris.size() > 1) {
log.warn("Found more than one DataDistributor for '" + action + "': " + uris);
}

return uris.get(0);
}

private DataDistributor instantiateDistributor(HttpServletRequest req, String distributorUri, Model model)
throws ActionFailedException {
try {
return new ConfigurationBeanLoader(model, req).loadInstance(distributorUri, DataDistributor.class);
} catch (ConfigurationBeanLoaderException e) {
throw new ActionFailedException("Failed to instantiate the DataDistributor: " + distributorUri, e);
}
}

public static DataDistributor instantiateDistributor(String distributorUri, Model model)
throws ActionFailedException {
try {
return new ConfigurationBeanLoader(model).loadInstance(distributorUri, DataDistributor.class);
} catch (ConfigurationBeanLoaderException e) {
throw new ActionFailedException("Failed to instantiate the DataDistributor: " + distributorUri, e);
}
}

private void runIt(HttpServletRequest req, HttpServletResponse resp, DataDistributor instance)
throws DataDistributorException {
try {
instance.init(new DataDistributorContextImpl(req));
log.debug("Distributor is " + instance);

resp.setContentType(instance.getContentType());
resp.setCharacterEncoding("UTF-8");
instance.writeOutput(resp.getOutputStream());
} catch (Exception e) {
log.error("Failed to execute the DataDistributor", e);
instance.close();
throw new ActionFailedException(e);
}
}

private void do400BadRequest(String message, HttpServletResponse resp) throws IOException {
log.debug("400BadRequest: " + message);
resp.setStatus(400);
resp.getWriter().println(message);
}

private void do403Forbidden(HttpServletResponse resp) throws IOException {
log.debug("403Forbidden");
resp.setStatus(403);
resp.getWriter().println("Not authorized for this action.");
}

private void do500InternalServerError(String message, Exception e, HttpServletResponse resp) throws IOException {
log.warn("500InternalServerError " + message, e);
resp.setStatus(500);
try {
PrintWriter w = resp.getWriter();
w.println(message);
e.printStackTrace(w);
} catch (IllegalStateException e1) {
OutputStream os = resp.getOutputStream();
os.write((message + "\n").getBytes());
e.printStackTrace(new PrintStream(os));
}

}

/**
* If you want to use a post form, go ahead.
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */

package edu.cornell.library.scholars.webapp.controller.api.distribute;

import java.util.Map;

import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;

/**
* TODO
*/
public abstract class AbstractDataDistributor implements DataDistributor {
private static final String ACTION_NAME_PROPERTY = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#actionName";

protected DataDistributorContext ddContext;
protected Map<String, String[]> parameters;
protected String actionName;

@Override
public void init(DataDistributorContext ddc) throws DataDistributorException {
this.ddContext = ddc;
this.parameters = ddc.getRequestParameters();
}

@Property(uri = ACTION_NAME_PROPERTY, minOccurs = 1, maxOccurs = 1)
public void setActionName(String aName) {
this.actionName = aName;
}

}
Loading