Merge pull request #666 from ctu-developers/master

DS-2168: New REST api with CRUD operations.
This commit is contained in:
Peter Dietz
2014-10-20 12:02:24 -04:00
19 changed files with 4837 additions and 527 deletions

View File

@@ -7,143 +7,849 @@
*/
package org.dspace.rest;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.DSpaceObject;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.rest.common.Bitstream;
import org.dspace.usage.UsageEvent;
import org.dspace.utils.DSpace;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.sql.SQLException;
import javax.ws.rs.core.Response.Status;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.eperson.Group;
import org.dspace.rest.common.Bitstream;
import org.dspace.rest.common.ResourcePolicy;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.storage.bitstore.BitstreamStorageManager;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.usage.UsageEvent;
/**
* Created with IntelliJ IDEA.
* User: peterdietz
* Date: 10/2/13
* Time: 5:56 PM
* To change this template use File | Settings | File Templates.
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*/
// Every DSpace class used without namespace is from package
// org.dspace.rest.common.*. Otherwise namespace is defined.
@Path("/bitstreams")
public class BitstreamResource {
Logger log = Logger.getLogger(BitstreamResource.class);
public class BitstreamResource extends Resource
{
private static final boolean writeStatistics;
static{
writeStatistics=ConfigurationManager.getBooleanProperty("rest","stats",false);
}
//BitstreamList - Not Implemented
private static Logger log = Logger.getLogger(BitstreamResource.class);
/**
* Return bitstream properties without file data. It can throws
* WebApplicationException with three response codes. Response code
* NOT_FOUND(404) or UNAUTHORIZED(401) or INTERNAL_SERVER_ERROR(500). Bad
* request is when id of bitstream does not exist. UNAUTHORIZED is if logged
* user into DSpace context have not permission to access bitstream. Server
* error when something went wrong.
*
* @param bitstreamId
* Id of bitstream in DSpace.
* @param expand
* This string defined, which additional options will be added
* into bitstream. Single options are separated by commas without
* space. Options are: "all", "parent".
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return If user is allowed to read bitstream, it returns instance of
* bitstream. Otherwise, it throws WebApplicationException with
* response code UNAUTHORIZED.
* @throws WebApplicationException
* It can happen on: Bad request, unauthorized, SQL exception
* and context exception(could not create context).
*/
@GET
@Path("/{bitstream_id}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Bitstream getBitstream(@PathParam("bitstream_id") Integer bitstream_id, @QueryParam("expand") String expand) {
public Bitstream getBitstream(@PathParam("bitstream_id") Integer bitstreamId, @QueryParam("expand") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading bitstream(id=" + bitstreamId + ") metadata.");
org.dspace.core.Context context = null;
try {
context = new org.dspace.core.Context();
Bitstream bitstream = null;
org.dspace.content.Bitstream bitstream = org.dspace.content.Bitstream.find(context, bitstream_id);
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.READ);
if(AuthorizeManager.authorizeActionBoolean(context, bitstream, org.dspace.core.Constants.READ)) {
return new org.dspace.rest.common.Bitstream(bitstream, expand);
} else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
} catch(SQLException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} finally {
if(context != null) {
try {
writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor, headers,
request);
bitstream = new Bitstream(dspaceBitstream, expand);
context.complete();
} catch (SQLException e) {
log.error(e.getMessage() + " occurred while trying to close");
log.trace("Bitsream(id=" + bitstreamId + ") was successfully read.");
}
catch (SQLException e)
{
processException("Someting went wrong while reading bitstream(id=" + bitstreamId + ") from database! Message: " + e,
context);
}
catch (ContextException e)
{
processException("Someting went wrong while reading bitstream(id=" + bitstreamId + "), ContextException. Message: "
+ e.getMessage(), context);
}
finally
{
processFinally(context);
}
return bitstream;
}
/**
* Return all bitstream resource policies from all bundles, in which
* bitstream is.
*
* @param bitstreamId
* Id of bitstream in DSpace.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return It returns array of ResourcePolicy.
*/
@GET
@Path("/{bitstream_id}/policy")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public ResourcePolicy[] getBitstreamPolicies(@PathParam("bitstream_id") Integer bitstreamId, @Context HttpHeaders headers)
{
log.info("Reading bitstream(id=" + bitstreamId + ") policies.");
org.dspace.core.Context context = null;
List<ResourcePolicy> policies = new ArrayList<ResourcePolicy>();
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.READ);
Bundle[] bundles = dspaceBitstream.getBundles();
for (Bundle bundle : bundles)
{
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundle.getBitstreamPolicies();
for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies)
{
if (policy.getResourceID() == bitstreamId)
{
policies.add(new ResourcePolicy(policy));
}
}
}
context.complete();
log.trace("Policies for bitsream(id=" + bitstreamId + ") was successfully read.");
}
catch (SQLException e)
{
processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId
+ "), SQLException! Message: " + e, context);
}
catch (ContextException e)
{
processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId
+ "), ContextException. Message: " + e.getMessage(), context);
}
finally
{
processFinally(context);
}
return policies.toArray(new ResourcePolicy[0]);
}
/**
* Read list of bitstreams. It throws WebApplicationException with response
* code INTERNAL_SERVER_ERROR(500), if there was problem while reading
* bitstreams from database.
*
* @param limit
* How much bitstreams in list will be. Default value is 100.
* @param offset
* On which index will list starts. Default values is 0.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return It returns array of bistreams. In array are not bitstreams on
* which user has not permission to read.
* @throws WebApplicationException
* It is thrown when was problem with database reading or with
* creating context.
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Bitstream[] getBitstreams(@QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading bitstreams.(offset=" + offset + ",limit=" + limit + ")");
org.dspace.core.Context context = null;
List<Bitstream> bitstreams = new ArrayList<Bitstream>();
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream[] dspaceBitstreams = org.dspace.content.Bitstream.findAll(context);
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0)))
{
log.warn("Pagging was badly set.");
limit = 100;
offset = 0;
}
// TODO If bitsream doesnt not exist it throw exception.
for (int i = offset; (i < (offset + limit)) && (i < dspaceBitstreams.length); i++)
{
if (AuthorizeManager.authorizeActionBoolean(context, dspaceBitstreams[i], org.dspace.core.Constants.READ))
{
if (dspaceBitstreams[i].getParentObject() != null)
{ // To eliminate bitstreams which cause exception, because of
// reading under administrator permissions
bitstreams.add(new Bitstream(dspaceBitstreams[i], expand));
writeStats(dspaceBitstreams[i], UsageEvent.Action.VIEW, user_ip, user_agent,
xforwarderfor, headers, request);
}
}
}
context.complete();
log.trace("Bitstreams were successfully read.");
}
catch (SQLException e)
{
processException("Something get wrong while reading bitstreams from database!. Message: " + e, context);
}
catch (ContextException e)
{
processException("Something get wrong while reading bitstreams, ContextException. Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
return bitstreams.toArray(new Bitstream[0]);
}
/**
* Read bitstream data. It can throw WebApplicationException with code
* INTERNAL_SERVER_ERROR(500). Caused by three exceptions: IOException if
* there was problem with reading bitstream file. SQLException if there was
* problem while reading from database. And AuthorizeException if there was
* problem with authorization of user logged to DSpace context.
*
* @param bitstreamId
* Id of bitstream, of which will be read data.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return Returns response with data with content type of file. It can
* return response code NOT_FOUND(404) if there was bad id of
* bitstream. Or response code UNAUTHORIZED(401) if user is not
* allowed to read bitstream.
* @throws WebApplicationException
* It is throw in this cases: When was problem with reading file
* data. Or was problem with database reading. Or was problem
* with creating context. Or problem with authorization.
*/
@GET
@Path("/{bitstream_id}/retrieve")
public javax.ws.rs.core.Response getFile(@PathParam("bitstream_id") final Integer bitstream_id,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) {
public javax.ws.rs.core.Response getBitstreamData(@PathParam("bitstream_id") Integer bitstreamId,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading data of bitstream(id=" + bitstreamId + ").");
org.dspace.core.Context context = null;
try {
context = new org.dspace.core.Context();
InputStream inputStream = null;
String type = null;
org.dspace.content.Bitstream bitstream = org.dspace.content.Bitstream.find(context, bitstream_id);
if(AuthorizeManager.authorizeActionBoolean(context, bitstream, org.dspace.core.Constants.READ)) {
if(writeStatistics){
writeStats(context, bitstream_id, user_ip, user_agent, xforwarderfor, headers, request);
}
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.READ);
return Response.ok(bitstream.retrieve()).type(bitstream.getFormat().getMIMEType()).build();
} else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor, headers,
request);
log.trace("Bitsream(id=" + bitstreamId + ") data was successfully read.");
inputStream = dspaceBitstream.retrieve();
type = dspaceBitstream.getFormat().getMIMEType();
} catch (IOException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} catch (SQLException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} catch (AuthorizeException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
} finally {
if(context != null) {
try {
context.complete();
} catch (SQLException e) {
log.error(e.getMessage() + " occurred while trying to close");
}
catch (IOException e)
{
processException("Could not read file of bitstream(id=" + bitstreamId + ")! Message: " + e, context);
}
catch (SQLException e)
{
processException("Something get wrong while reading bitsream(id=" + bitstreamId + ") from database! Message: " + e,
context);
}
catch (AuthorizeException e)
{
processException("Could not retrieve file of bitstream(id=" + bitstreamId + "), AuthorizeException! Message: " + e,
context);
}
catch (ContextException e)
{
processException(
"Could not retrieve file of bitstream(id=" + bitstreamId + "), ContextException! Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
return Response.ok(inputStream).type(type).build();
}
/**
* Add bitstream policy to all bundles in which bitstream is.
*
* @param bitstreamId
* Id of bitstream in DSpace.
* @param policy
* Policy which will be added. But this atributes does not be
* applied: epersonId,
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return Returns ok, if was all ok. Otherwise status code 500.
*/
@POST
@Path("/{bitstream_id}/policy")
public javax.ws.rs.core.Response addBitstreamPolicy(@PathParam("bitstream_id") Integer bitstreamId, ResourcePolicy policy,
@Context HttpHeaders headers)
{
log.info("Adding bitstream(id=" + bitstreamId + ") READ policy with permission for group(id=" + policy.getGroupId()
+ ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.READ);
Bundle[] bundles = dspaceBitstream.getBundles();
for (Bundle bundle : bundles)
{
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundle.getBitstreamPolicies();
org.dspace.authorize.ResourcePolicy dspacePolicy = org.dspace.authorize.ResourcePolicy.create(context);
dspacePolicy.setAction(policy.getActionInt());
dspacePolicy.setGroup(Group.find(context, policy.getGroupId()));
dspacePolicy.setResourceID(dspaceBitstream.getID());
dspacePolicy.setResource(dspaceBitstream);
dspacePolicy.setResourceType(org.dspace.core.Constants.BITSTREAM);
dspacePolicy.setStartDate(policy.getStartDate());
dspacePolicy.setEndDate(policy.getEndDate());
dspacePolicy.setRpDescription(policy.getRpDescription());
dspacePolicy.setRpName(policy.getRpName());
dspacePolicy.update();
// dspacePolicy.setRpType(org.dspace.authorize.ResourcePolicy.TYPE_CUSTOM);
bitstreamsPolicies.add(dspacePolicy);
bundle.replaceAllBitstreamPolicies(bitstreamsPolicies);
bundle.update();
}
context.complete();
log.trace("Policy for bitsream(id=" + bitstreamId + ") was successfully added.");
}
catch (SQLException e)
{
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
+ "), SQLException! Message: " + e, context);
}
catch (ContextException e)
{
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
+ "), ContextException. Message: " + e.getMessage(), context);
}
catch (AuthorizeException e)
{
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
+ "), AuthorizeException! Message: " + e, context);
}
finally
{
processFinally(context);
}
return Response.status(Status.OK).build();
}
/**
* Update bitstream metadata. It replace everything on targeted bitstream.
* It can throws WebApplicationException caused by two exceptions:
* SQLException, if there was problem with database. AuthorizeException if
* there was problem with authorization to edit bitstream metadata.
*
* @param bitstreamId
* Id of bistream, wich will be updated.
* @param bitstream
* Bitstream with will be placed. It muset have filled user
* creditials.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return Return response codes: OK(200), NOT_FOUND(404) if bitstream does
* not exist and UNAUTHORIZED(401) if user is not allowed to write
* to bitstream.
* @throws WebApplicationException
* It can be thrown by: Error in reading from database. Or
* creating context or with authorization to bitstream.
*/
@PUT
@Path("/{bitstream_id}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response updateBitstream(@PathParam("bitstream_id") Integer bitstreamId, Bitstream bitstream,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Updating bitstream(id=" + bitstreamId + ") metadata.");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.WRITE);
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwarderfor,
headers, request);
log.trace("Updating bitstream metadata.");
dspaceBitstream.setDescription(bitstream.getDescription());
if (getMimeType(bitstream.getName()) == null)
{
dspaceBitstream.setFormat(BitstreamFormat.findUnknown(context));
}
else
{
dspaceBitstream.setFormat(BitstreamFormat.findByMIMEType(context, getMimeType(bitstream.getName())));
}
dspaceBitstream.setName(bitstream.getName());
Integer sequenceId = bitstream.getSequenceId();
if (sequenceId != null && sequenceId.intValue() != -1)
{
dspaceBitstream.setSequenceID(sequenceId);
}
dspaceBitstream.update();
if (bitstream.getPolicies() != null)
{
Bundle[] bundles = dspaceBitstream.getBundles();
ResourcePolicy[] policies = bitstream.getPolicies();
for (Bundle bundle : bundles)
{
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundle.getBitstreamPolicies();
// Remove old bitstream policies
List<org.dspace.authorize.ResourcePolicy> policiesToRemove = new ArrayList<org.dspace.authorize.ResourcePolicy>();
for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies)
{
if (policy.getResourceID() == dspaceBitstream.getID())
{
policiesToRemove.add(policy);
}
}
for (org.dspace.authorize.ResourcePolicy policy : policiesToRemove)
{
bitstreamsPolicies.remove(policy);
}
// Add all new bitstream policies
for (ResourcePolicy policy : policies)
{
org.dspace.authorize.ResourcePolicy dspacePolicy = org.dspace.authorize.ResourcePolicy.create(context);
dspacePolicy.setAction(policy.getActionInt());
dspacePolicy.setGroup(Group.find(context, policy.getGroupId()));
dspacePolicy.setResourceID(dspaceBitstream.getID());
dspacePolicy.setResource(dspaceBitstream);
dspacePolicy.setResourceType(org.dspace.core.Constants.BITSTREAM);
dspacePolicy.setStartDate(policy.getStartDate());
dspacePolicy.setEndDate(policy.getEndDate());
dspacePolicy.setRpDescription(policy.getRpDescription());
dspacePolicy.setRpName(policy.getRpName());
dspacePolicy.update();
bitstreamsPolicies.add(dspacePolicy);
}
bundle.replaceAllBitstreamPolicies(bitstreamsPolicies);
bundle.update();
}
}
private void writeStats(org.dspace.core.Context context, Integer bitstream_id, String user_ip, String user_agent,
String xforwarderfor, HttpHeaders headers,
HttpServletRequest request) {
context.complete();
try{
DSpaceObject bitstream = DSpaceObject.find(context, Constants.BITSTREAM, bitstream_id);
if(user_ip==null || user_ip.length()==0){
new DSpace().getEventService().fireEvent(
new UsageEvent(
UsageEvent.Action.VIEW,
request,
context,
bitstream));
} else{
new DSpace().getEventService().fireEvent(
new UsageEvent(
UsageEvent.Action.VIEW,
user_ip,
user_agent,
xforwarderfor,
context,
bitstream));
}
log.debug("fired event");
catch (SQLException e)
{
processException("Could not update bitstream(id=" + bitstreamId + ") metadata, SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not update bitstream(id=" + bitstreamId + ") metadata, AuthorizeException. Message: " + e,
context);
}
catch (ContextException e)
{
processException(
"Could not update bitstream(id=" + bitstreamId + ") metadata, ContextException. Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
} catch(SQLException ex){
log.error("SQL exception can't write usageEvent \n" + ex);
log.info("Bitstream metadata(id=" + bitstreamId + ") were successfully updated.");
return Response.ok().build();
}
/**
* Update bitstream data. It change bitstream data by editing database rows.
* It can throw WebApplicationException caused by: SQLException if there was
* problem with database editing or reading, IOException if there was
* problem with reading from inputstream, Exception if there was another
* problem.
*
* @param bitstreamId
* Id of bistream, which will be updated.
* @param is
* Inputstream filled with new data.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return Return response if bitstream was updated. Response codes:
* OK(200), NOT_FOUND(404) if id of bitstream was bad. And
* UNAUTHORIZED(401) if user is not allowed to update bitstream.
* @throws WebApplicationException
* This exception can be thrown in this cases: Problem with
* reading or writing to database. Or problem with reading from
* inputstream.
*/
// TODO Change to better logic, without editing database.
@PUT
@Path("/{bitstream_id}/data")
public Response updateBitstreamData(@PathParam("bitstream_id") Integer bitstreamId, InputStream is,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Updating bitstream(id=" + bitstreamId + ") data.");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.WRITE);
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwarderfor,
headers, request);
log.trace("Creating new bitstream.");
int newBitstreamId = BitstreamStorageManager.store(context, is);
log.trace("Looking for table rows of bitstreams.");
TableRow originalBitstreamRow = DatabaseManager.find(context, "Bitstream", bitstreamId);
TableRow bitstream = DatabaseManager.find(context, "Bitstream", newBitstreamId);
log.trace("Changing new internal id with old internal id.");
String internal_id = originalBitstreamRow.getStringColumn("internal_id");
Long size_bytes = originalBitstreamRow.getLongColumn("size_bytes");
originalBitstreamRow.setColumn("internal_id", bitstream.getStringColumn("internal_id"));
originalBitstreamRow.setColumn("size_bytes", bitstream.getLongColumn("size_bytes"));
bitstream.setColumn("internal_id", internal_id);
bitstream.setColumn("size_bytes", size_bytes);
DatabaseManager.update(context, originalBitstreamRow);
BitstreamStorageManager.delete(context, newBitstreamId);
context.complete();
}
catch (SQLException e)
{
processException("Could not update bitstream(id=" + bitstreamId + ") data, SQLException. Message: " + e, context);
}
catch (IOException e)
{
processException("Could not update bitstream(id=" + bitstreamId + ") data, IOException. Message: " + e, context);
}
catch (ContextException e)
{
processException(
"Could not update bitstream(id=" + bitstreamId + ") data, ContextException. Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.info("Bitstream(id=" + bitstreamId + ") data was successfully updated.");
return Response.ok().build();
}
/**
* Delete bitstream from all bundles in dspace. It can throw
* WebApplicationException, which can be caused by three exceptions.
* SQLException if there was problem with reading from database or removing
* from database. AuthorizeException, if user has not permission to delete
* bitstream or file. IOException, if there was problem with file deleting.
*
* @param bitstreamId
* Id of bitsream, which will be deleted.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return Return response codes: OK(200), NOT_FOUND(404) if bitstream of
* that id does not exist and UNAUTHORIZED(401) if user is not
* allowed to delete bitstream.
* @throws WebApplicationException
* It can be throw when was problem with reading or editting
* database. Or problem with file deleting. Or problem with
* authorization to bitstream and bundles. Or problem with
* creating context.
*/
@DELETE
@Path("/{bitstream_id}")
public Response deleteBitstream(@PathParam("bitstream_id") Integer bitstreamId, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Deleting bitstream(id=" + bitstreamId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.DELETE);
writeStats(dspaceBitstream, UsageEvent.Action.DELETE, user_ip, user_agent, xforwarderfor,
headers, request);
log.trace("Deleting bitstream from all bundles.");
for (org.dspace.content.Bundle bundle : dspaceBitstream.getBundles())
{
org.dspace.content.Bundle.find(context, bundle.getID()).removeBitstream(dspaceBitstream);
}
context.complete();
}
catch (SQLException e)
{
processException("Could not delete bitstream(id=" + bitstreamId + "), SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not delete bitstream(id=" + bitstreamId + "), AuthorizeException. Message: " + e, context);
}
catch (IOException e)
{
processException("Could not delete bitstream(id=" + bitstreamId + "), IOException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not delete bitstream(id=" + bitstreamId + "), ContextException. Message:" + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.info("Bitstream(id=" + bitstreamId + ") was successfully deleted.");
return Response.ok().build();
}
/**
* Delete policy.
*
* @param bitstreamId
* Id of bitstream in dspace, which policy will be deleted.
* @param policyId
* Id of policy which will be deleted.
* @param headers
* If you want to access to item under logged user into context.
* In headers must be set header "rest-dspace-token" with passed
* token from login method.
* @return It returns Ok, if was all ok. Otherwise status code 500.
*/
@DELETE
@Path("/{bitstream_id}/policy/{policy_id}")
public javax.ws.rs.core.Response deleteBitstreamPolicy(@PathParam("bitstream_id") Integer bitstreamId,
@PathParam("policy_id") Integer policyId, @Context HttpHeaders headers)
{
log.info("Deleting bitstream(id=" + bitstreamId + ") READ policy(id=" + policyId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, org.dspace.core.Constants.READ);
Bundle[] bundles = dspaceBitstream.getBundles();
for (Bundle bundle : bundles)
{
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundle.getBitstreamPolicies();
for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies)
{
if (policy.getID() == policyId.intValue())
{
bitstreamsPolicies.remove(policy);
break;
}
}
bundle.replaceAllBitstreamPolicies(bitstreamsPolicies);
bundle.update();
}
context.complete();
log.trace("Policy for bitsream(id=" + bitstreamId + ") was successfully added.");
}
catch (SQLException e)
{
processException("Someting went wrong while deleting READ policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
+ "), SQLException! Message: " + e, context);
}
catch (ContextException e)
{
processException("Someting went wrong while deleting READ policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
+ "), ContextException. Message: " + e.getMessage(), context);
}
catch (AuthorizeException e)
{
processException("Someting went wrong while deleting READ policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
+ "), AuthorizeException! Message: " + e, context);
}
finally
{
processFinally(context);
}
return Response.status(Status.OK).build();
}
/**
* Return type of file in MIME, by file extension.
*
* @param name
* Name of file.
* @return String filled with type of file in MIME style.
*/
static String getMimeType(String name)
{
return URLConnection.guessContentTypeFromName(name);
}
/**
* Find bitstream from DSpace database. It is encapsulation of method
* org.dspace.content.Bitstream.find with checking if item exist and if user
* logged into context has permission to do passed action.
*
* @param context
* Context of actual logged user.
* @param id
* Id of bitstream in DSpace.
* @param action
* Constant from org.dspace.core.Constants.
* @return It returns DSpace bitstream.
* @throws WebApplicationException
* Is thrown when item with passed id is not exists and if user
* has no permission to do passed action.
*/
private org.dspace.content.Bitstream findBitstream(org.dspace.core.Context context, int id, int action)
throws WebApplicationException
{
org.dspace.content.Bitstream bitstream = null;
try
{
bitstream = org.dspace.content.Bitstream.find(context, id);
if ((bitstream == null) || (bitstream.getParentObject() == null))
{
context.abort();
log.warn("Bitstream(id=" + id + ") was not found!");
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else if (!AuthorizeManager.authorizeActionBoolean(context, bitstream, action))
{
context.abort();
if (context.getCurrentUser() != null)
{
log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to "
+ getActionString(action) + " bitstream!");
}
else
{
log.error("User(anonymous) has not permission to " + getActionString(action) + " bitsteam!");
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}
catch (SQLException e)
{
processException("Something get wrong while finding bitstream. SQLException, Message:" + e, context);
}
return bitstream;
}
}

View File

@@ -7,149 +7,767 @@
*/
package org.dspace.rest;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.DSpaceObject;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.rest.common.Collection;
import org.dspace.usage.UsageEvent;
import org.dspace.utils.DSpace;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/*
The "Path" annotation indicates the URI this class will be available at relative to your base URL. For
example, if this web-app is launched at localhost using a context of "hello" and no URL pattern is defined
in the web.xml servlet mapping section, then the web service will be available at:
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
http://localhost:8080/<webapp>/collections
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.browse.BrowseException;
import org.dspace.rest.common.Collection;
import org.dspace.rest.common.Item;
import org.dspace.rest.common.MetadataEntry;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.usage.UsageEvent;
/**
* This class provides all CRUD operation over collections.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*/
@Path("/collections")
public class CollectionsResource {
public class CollectionsResource extends Resource
{
private static Logger log = Logger.getLogger(CollectionsResource.class);
@javax.ws.rs.core.Context ServletContext servletContext;
private static final boolean writeStatistics;
static{
writeStatistics=ConfigurationManager.getBooleanProperty("rest","stats",false);
}
@GET
@Path("/")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public org.dspace.rest.common.Collection[] list(@QueryParam("expand") String expand, @QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset) {
org.dspace.core.Context context = null;
try {
context = new org.dspace.core.Context();
org.dspace.content.Collection[] collections;
//Only support paging if limit/offset are 0 or positive values.
if(limit != null && limit >= 0 && offset != null && offset >= 0) {
collections = org.dspace.content.Collection.findAll(context, limit, offset);
} else {
collections = org.dspace.content.Collection.findAll(context);
}
ArrayList<org.dspace.rest.common.Collection> collectionArrayList = new ArrayList<org.dspace.rest.common.Collection>();
for(org.dspace.content.Collection collection : collections) {
if(AuthorizeManager.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) {
org.dspace.rest.common.Collection restCollection = new org.dspace.rest.common.Collection(collection, null, context, limit, offset);
collectionArrayList.add(restCollection);
} // Not showing restricted-access collections
}
return collectionArrayList.toArray(new org.dspace.rest.common.Collection[0]);
} catch (SQLException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} finally {
if(context != null) {
try {
context.complete();
} catch (SQLException e) {
log.error(e.getMessage() + " occurred while trying to close");
}
}
}
}
/**
* Return instance of collection with passed id. You can add more properties
* through expand parameter.
*
* @param collectionId
* Id of collection in DSpace.
* @param expand
* String in which is what you want to add to returned instance
* of collection. Options are: "all", "parentCommunityList",
* "parentCommunity", "items", "license" and "logo". If you want
* to use multiple options, it must be separated by commas.
* @param limit
* Limit value for items in list in collection. Default value is
* 100.
* @param offset
* Offset of start index in list of items of collection. Default
* value is 0.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of collection. It can also return status code
* NOT_FOUND(404) if id of collection is incorrect or status code
* UNATHORIZED(401) if user has no permission to read collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException). It is thrown by NOT_FOUND and
* UNATHORIZED status codes, too.
*/
@GET
@Path("/{collection_id}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public org.dspace.rest.common.Collection getCollection(@PathParam("collection_id") Integer collection_id, @QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) {
org.dspace.core.Context context = null;
try {
context = new org.dspace.core.Context();
public org.dspace.rest.common.Collection getCollection(@PathParam("collection_id") Integer collectionId,
@QueryParam("expand") String expand, @QueryParam("limit") @DefaultValue("100") Integer limit,
@QueryParam("offset") @DefaultValue("0") Integer offset, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Reading collection(id=" + collectionId + ").");
org.dspace.core.Context context = null;
Collection collection = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, org.dspace.core.Constants.READ);
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor,
headers, request);
collection = new Collection(dspaceCollection, expand, context, limit, offset);
context.complete();
org.dspace.content.Collection collection = org.dspace.content.Collection.find(context, collection_id);
if(AuthorizeManager.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) {
if(writeStatistics){
writeStats(context, collection_id, user_ip, user_agent, xforwarderfor, headers, request);
}
return new org.dspace.rest.common.Collection(collection, expand, context, limit, offset);
} else {
catch (SQLException e)
{
processException("Could not read collection(id=" + collectionId + "), SQLException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not read collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.trace("Collection(id=" + collectionId + ") has been successfully read.");
return collection;
}
/**
* Return array of all collections in DSpace. You can add more properties
* through expand parameter.
*
* @param expand
* String in which is what you want to add to returned instance
* of collection. Options are: "all", "parentCommunityList",
* "parentCommunity", "items", "license" and "logo". If you want
* to use multiple options, it must be separated by commas.
* @param limit
* Limit value for items in list in collection. Default value is
* 100.
* @param offset
* Offset of start index in list of items of collection. Default
* value is 0.
* @param headers
* If you want to access to collections under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return array of collection, on which has logged user permission
* to view.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException).
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public org.dspace.rest.common.Collection[] getCollections(@QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading all collections.(offset=" + offset + ",limit=" + limit + ")");
org.dspace.core.Context context = null;
List<Collection> collections = new ArrayList<Collection>();
try
{
context = createContext(getUser(headers));
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0)))
{
log.warn("Paging was badly set.");
limit = 100;
offset = 0;
}
org.dspace.content.Collection[] dspaceCollections = org.dspace.content.Collection.findAll(context, limit, offset);
for(org.dspace.content.Collection dspaceCollection : dspaceCollections)
{
if (AuthorizeManager.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ))
{
Collection collection = new org.dspace.rest.common.Collection(dspaceCollection, null, context, limit,
offset);
collections.add(collection);
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent,
xforwarderfor, headers, request);
}
}
context.complete();
}
catch (SQLException e)
{
processException("Something went wrong while reading collections from database. Message: " + e, context);
}
catch (ContextException e)
{
processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("All collections were successfully read.");
return collections.toArray(new org.dspace.rest.common.Collection[0]);
}
/**
* Return array of items in collection. You can add more properties to items
* with expand parameter.
*
* @param collectionId
* Id of collection in DSpace.
* @param expand
* String which define, what additional properties will be in
* returned item. Options are separeted by commas and are: "all",
* "metadata", "parentCollection", "parentCollectionList",
* "parentCommunityList" and "bitstreams".
* @param limit
* Limit value for items in array. Default value is 100.
* @param offset
* Offset of start index in array of items of collection. Default
* value is 0.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return array of items, on which has logged user permission to
* read. It can also return status code NOT_FOUND(404) if id of
* collection is incorrect or status code UNATHORIZED(401) if user
* has no permission to read collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException). It is thrown by NOT_FOUND and
* UNATHORIZED status codes, too.
*/
@GET
@Path("/{collection_id}/items")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public org.dspace.rest.common.Item[] getCollectionItems(@PathParam("collection_id") Integer collectionId,
@QueryParam("expand") String expand, @QueryParam("limit") @DefaultValue("100") Integer limit,
@QueryParam("offset") @DefaultValue("0") Integer offset, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Reading collection(id=" + collectionId + ") items.");
org.dspace.core.Context context = null;
List<Item> items = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, org.dspace.core.Constants.READ);
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor,
headers, request);
items = new ArrayList<Item>();
org.dspace.content.ItemIterator dspaceItems = dspaceCollection.getItems();
for (int i = 0; (dspaceItems.hasNext()) && (i < (limit + offset)); i++)
{
if (i >= offset)
{
org.dspace.content.Item dspaceItem = dspaceItems.next();
if (AuthorizeManager.authorizeActionBoolean(context, dspaceItem, org.dspace.core.Constants.READ))
{
items.add(new Item(dspaceItem, expand, context));
writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor,
headers, request);
}
}
}
context.complete();
}
catch (SQLException e)
{
processException("Could not read collection items, SQLException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not read collection items, ContextException. Message: " + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("All items in collection(id=" + collectionId + ") were successfully read.");
return items.toArray(new Item[0]);
}
/**
* Create item in collection. Item can be without filled metadata.
*
* @param collectionId
* Id of collection in which will be item created.
* @param item
* Item filled only with metadata, other variables are ignored.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return status code with item. Return status (OK)200 if item was
* created. NOT_FOUND(404) if id of collection does not exists.
* UNAUTHORIZED(401) if user have not permission to write items in
* collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading or
* writing (SQLException) or problem with creating
* context(ContextException) or problem with authorization to
* collection or IOException or problem with index item into
* browse index. It is thrown by NOT_FOUND and UNATHORIZED
* status codes, too.
*
*/
@POST
@Path("/{collection_id}/items")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Item addCollectionItem(@PathParam("collection_id") Integer collectionId, Item item,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Create item in collection(id=" + collectionId + ").");
org.dspace.core.Context context = null;
Item returnItem = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
org.dspace.core.Constants.WRITE);
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwarderfor,
headers, request);
log.trace("Creating item in collection(id=" + collectionId + ").");
org.dspace.content.WorkspaceItem workspaceItem = org.dspace.content.WorkspaceItem.create(context, dspaceCollection,
false);
org.dspace.content.Item dspaceItem = workspaceItem.getItem();
log.trace("Adding metadata to item(id=" + dspaceItem.getID() + ").");
if (item.getMetadata() != null)
{
for (MetadataEntry entry : item.getMetadata())
{
String data[] = mySplit(entry.getKey());
dspaceItem.addMetadata(data[0], data[1], data[2], entry.getLanguage(), entry.getValue());
}
}
workspaceItem.update();
// Index item to browse.
org.dspace.browse.IndexBrowse browse = new org.dspace.browse.IndexBrowse();
browse.indexItem(dspaceItem);
log.trace("Installing item to collection(id=" + collectionId + ").");
dspaceItem = org.dspace.content.InstallItem.installItem(context, workspaceItem);
returnItem = new Item(dspaceItem, "", context);
context.complete();
}
catch (SQLException e)
{
processException("Could not add item into collection(id=" + collectionId + "), SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not add item into collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
context);
}
catch (IOException e)
{
processException("Could not add item into collection(id=" + collectionId + "), IOException. Message: " + e, context);
}
catch (BrowseException e)
{
processException("Could not add item into browse index, BrowseException. Message: " + e, context);
}
catch (ContextException e)
{
processException(
"Could not add item into collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.info("Item successfully created in collection(id=" + collectionId + "). Item handle=" + returnItem.getHandle());
return returnItem;
}
/**
* Update collection. It replace all properties.
*
* @param collectionId
* Id of collection in DSpace.
* @param collection
* Collection which will replace properties of actual collection.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response 200 if was everything all right. Otherwise 400
* when id of community was incorrect or 401 if was problem with
* permission to write into collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading or
* writing. Or problem with authorization to collection. Or
* problem with creating context.
*/
@PUT
@Path("/{collection_id}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response updateCollection(@PathParam("collection_id") Integer collectionId,
org.dspace.rest.common.Collection collection, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Updating collection(id=" + collectionId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
org.dspace.core.Constants.WRITE);
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwarderfor,
headers, request);
dspaceCollection.setMetadata("name", collection.getName());
dspaceCollection.setLicense(collection.getLicense());
// dspaceCollection.setLogo(collection.getLogo()); // TODO Add this option.
dspaceCollection.setMetadata(org.dspace.content.Collection.COPYRIGHT_TEXT, collection.getCopyrightText());
dspaceCollection.setMetadata(org.dspace.content.Collection.INTRODUCTORY_TEXT, collection.getIntroductoryText());
dspaceCollection.setMetadata(org.dspace.content.Collection.SHORT_DESCRIPTION, collection.getShortDescription());
dspaceCollection.setMetadata(org.dspace.content.Collection.SIDEBAR_TEXT, collection.getSidebarText());
dspaceCollection.update();
context.complete();
}
catch (ContextException e)
{
processException("Could not update collection(id=" + collectionId + "), ContextEception. Message: " + e.getMessage(),
context);
}
catch (SQLException e)
{
processException("Could not update collection(id=" + collectionId + "), SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not update collection(id=" + collectionId + "), AuthorizeException. Message: " + e, context);
}
finally
{
processFinally(context);
}
log.info("Collection(id=" + collectionId + ") successfully updated.");
return Response.ok().build();
}
/**
* Delete collection.
*
* @param collectionId
* Id of collection which will be deleted.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response code OK(200) if was everything all right.
* Otherwise return NOT_FOUND(404) if was id of community or
* collection incorrect. Or (UNAUTHORIZED)401 if was problem with
* permission to community or collection.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading or writing. Or problem with deleting
* collection caused by IOException or authorization.
*/
@DELETE
@Path("/{collection_id}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response deleteCollection(@PathParam("collection_id") Integer collectionId, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Delete collection(id=" + collectionId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
org.dspace.core.Constants.DELETE);
writeStats(dspaceCollection, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwarderfor,
headers, request);
org.dspace.content.Community community = (org.dspace.content.Community) dspaceCollection.getParentObject();
community.removeCollection(dspaceCollection);
context.complete();
}
catch (ContextException e)
{
processException(
"Could not delete collection(id=" + collectionId + "), ContextExcpetion. Message: " + e.getMessage(), context);
}
catch (SQLException e)
{
processException("Could not delete collection(id=" + collectionId + "), SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not delete collection(id=" + collectionId + "), AuthorizeException. Message: " + e, context);
}
catch (IOException e)
{
processException("Could not delete collection(id=" + collectionId + "), IOException. Message: " + e, context);
}
finally
{
processFinally(context);
}
log.info("Collection(id=" + collectionId + ") was successfully deleted.");
return Response.ok().build();
}
/**
* Delete item in collection.
*
* @param collectionId
* Id of collection which will be deleted.
*
* @param itemId
* Id of item in colletion.
* @return It returns status code: OK(200). NOT_FOUND(404) if item or
* collection was not found, UNAUTHORIZED(401) if user is not
* allowed to delete item or permission to write into collection.
* @throws WebApplicationException
* It can be thrown by: SQLException, when was problem with
* database reading or writting. AuthorizeException, when was
* problem with authorization to item or collection.
* IOException, when was problem with removing item.
* ContextException, when was problem with creating context of
* DSpace.
*/
@DELETE
@Path("/{collection_id}/items/{item_id}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response deleteCollectionItem(@PathParam("collection_id") Integer collectionId, @PathParam("item_id") Integer itemId,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Delete item(id=" + itemId + ") in collection(id=" + collectionId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
org.dspace.core.Constants.WRITE);
org.dspace.content.Item item = null;
org.dspace.content.ItemIterator dspaceItems = dspaceCollection.getItems();
while (dspaceItems.hasNext())
{
org.dspace.content.Item dspaceItem = dspaceItems.next();
if (dspaceItem.getID() == itemId)
{
item = dspaceItem;
}
}
if (item == null)
{
context.abort();
log.warn("Item(id=" + itemId + ") was not found!");
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else if (!AuthorizeManager.authorizeActionBoolean(context, item, org.dspace.core.Constants.REMOVE))
{
context.abort();
if (context.getCurrentUser() != null)
{
log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to delete item!");
}
else
{
log.error("User(anonymous) has not permission to delete item!");
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
} catch (SQLException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} finally {
if(context != null) {
try {
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwarderfor,
headers, request);
writeStats(item, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwarderfor, headers, request);
dspaceCollection.removeItem(item);
context.complete();
} catch (SQLException e) {
log.error(e.getMessage() + " occurred while trying to close");
}
catch (ContextException e)
{
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
+ "), ContextException. Message: " + e.getMessage(), context);
}
catch (SQLException e)
{
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
+ "), SQLException. Message: " + e, context);
}
catch (AuthorizeException e)
{
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
+ "), AuthorizeException. Message: " + e, context);
}
catch (IOException e)
{
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
+ "), IOException. Message: " + e, context);
}
finally
{
processFinally(context);
}
log.info("Item(id=" + itemId + ") in collection(id=" + collectionId + ") was successfully deleted.");
return Response.ok().build();
}
/**
* Search for first collection with passed name.
*
* @param name
* Name of collection.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return It returns null if collection was not found. Otherwise returns
* first founded collection.
* @throws WebApplicationException
*/
@POST
@Path("/find-collection")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Collection findCollectionByName(String name, @Context HttpHeaders headers) throws WebApplicationException
{
log.info("Searching for first collection with name=" + name + ".");
org.dspace.core.Context context = null;
Collection collection = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.Collection[] dspaceCollections;
dspaceCollections = org.dspace.content.Collection.findAll(context);
for (org.dspace.content.Collection dspaceCollection : dspaceCollections)
{
if (AuthorizeManager.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ))
{
if (dspaceCollection.getName().equals(name))
{
collection = new Collection(dspaceCollection, "", context, 100, 0);
break;
}
}
}
private void writeStats(org.dspace.core.Context context, Integer collection_id, String user_ip, String user_agent,
String xforwarderfor, HttpHeaders headers,
HttpServletRequest request) {
context.complete();
try{
DSpaceObject collection = DSpaceObject.find(context, Constants.COLLECTION, collection_id);
if(user_ip==null || user_ip.length()==0){
new DSpace().getEventService().fireEvent(
new UsageEvent(
UsageEvent.Action.VIEW,
request,
context,
collection));
} else{
new DSpace().getEventService().fireEvent(
new UsageEvent(
UsageEvent.Action.VIEW,
user_ip,
user_agent,
xforwarderfor,
context,
collection));
}
log.debug("fired event");
catch (SQLException e)
{
processException("Something went wrong while searching for collection(name=" + name + ") from database. Message: "
+ e, context);
}
catch (ContextException e)
{
processException("Something went wrong while searching for collection(name=" + name + "), ContextError. Message: "
+ e.getMessage(), context);
}
finally
{
processFinally(context);
}
} catch(SQLException ex){
log.error("SQL exception can't write usageEvent \n" + ex);
if (collection == null)
{
log.info("Collection was not found.");
}
else
{
log.info("Collection was found with id(" + collection.getId() + ").");
}
return collection;
}
/**
* Find collection from DSpace database. It is encapsulation of method
* org.dspace.content.Collection.find with checking if item exist and if
* user logged into context has permission to do passed action.
*
* @param context
* Context of actual logged user.
* @param id
* Id of collection in DSpace.
* @param action
* Constant from org.dspace.core.Constants.
* @return It returns DSpace collection.
* @throws WebApplicationException
* Is thrown when item with passed id is not exists and if user
* has no permission to do passed action.
*/
private org.dspace.content.Collection findCollection(org.dspace.core.Context context, int id, int action)
throws WebApplicationException
{
org.dspace.content.Collection collection = null;
try
{
collection = org.dspace.content.Collection.find(context, id);
if (collection == null)
{
context.abort();
log.warn("Collection(id=" + id + ") was not found!");
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
else if (!AuthorizeManager.authorizeActionBoolean(context, collection, action))
{
context.abort();
if (context.getCurrentUser() != null)
{
log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to "
+ getActionString(action) + " collection!");
}
else
{
log.error("User(anonymous) has not permission to " + getActionString(action) + " collection!");
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}
catch (SQLException e)
{
processException("Something get wrong while finding collection(id=" + id + "). SQLException, Message: " + e, context);
}
return collection;
}
}

View File

@@ -32,14 +32,18 @@ import java.sql.SQLException;
@Path("/handle")
public class HandleResource {
private static Logger log = Logger.getLogger(HandleResource.class);
private static org.dspace.core.Context context;
@GET
@Path("/{prefix}/{suffix}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public org.dspace.rest.common.DSpaceObject getObject(@PathParam("prefix") String prefix, @PathParam("suffix") String suffix, @QueryParam("expand") String expand) {
org.dspace.core.Context context = null;
try {
context = new org.dspace.core.Context();
if(context == null || !context.isValid() ) {
context = new Context();
//Failed SQL is ignored as a failed SQL statement, prevent: current transaction is aborted, commands ignored until end of transaction block
context.getDBConnection().setAutoCommit(true);
}
org.dspace.content.DSpaceObject dso = HandleManager.resolveToObject(context, prefix + "/" + suffix);
if(dso == null) {
@@ -64,14 +68,6 @@ public class HandleResource {
} catch (SQLException e) {
log.error(e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
} finally {
if(context != null) {
try {
context.complete();
} catch (SQLException e) {
log.error(e.getMessage() + " occurred while trying to close");
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.core.ConfigurationManager;
import org.dspace.eperson.EPerson;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.usage.UsageEvent;
import org.dspace.utils.DSpace;
/**
* Superclass of all resource class in REST api. It has methods for creating
* context, write statistics, process exception, splitting key of metadata,
* string representation of action and method for getting user from header.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*
*/
public class Resource
{
private static Logger log = Logger.getLogger(Resource.class);
private static final boolean writeStatistics;
static
{
writeStatistics = ConfigurationManager.getBooleanProperty("rest", "stats", false);
}
/**
* Create context to work with DSpace database. It can create context
* without logged user (parameter user is null) or with. It can throws
* WebApplicationException caused by: SQLException, if there was problem
* with reading from database. AuthorizeException, if there was problem with
* authorization to read form database. And Exception, if there was some
* problem with creating context.
*
* @param person
* User which will be logged in context.
* @return New created context with logged user if user was not null.
* Otherwise, without logged user.
* @throws ContextException
* Throw if was problem to create context. It can be caused by
* SQLException, error in creating context or find user to log
* in. Or can be caused by AuthorizeException if was problem to
* authorize to find user.
*/
protected static org.dspace.core.Context createContext(EPerson person) throws ContextException
{
org.dspace.core.Context context = null;
try
{
context = new org.dspace.core.Context();
context.getDBConnection().setAutoCommit(false); // Disable autocommit.
if (person != null)
{
context.setCurrentUser(person);
}
return context;
}
catch (SQLException e)
{
if ((context != null) && (context.isValid()))
{
context.abort();
}
throw new ContextException("Could not create context, SQLException. Message: " + e, e);
}
}
/**
* It write statistic about using REST api.
* @param dspaceObject
* Object of DSpace which is performed.
* @param action
* What action is performed with object.
* @param user_ip
* @param user_agent
* @param xforwarderfor
* @param headers
* @param request
*/
protected void writeStats(DSpaceObject dspaceObject, UsageEvent.Action action,
String user_ip, String user_agent, String xforwarderfor, HttpHeaders headers, HttpServletRequest request)
{
if (!writeStatistics)
{
return;
}
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
if ((user_ip == null) || (user_ip.length() == 0))
{
new DSpace().getEventService().fireEvent(new UsageEvent(action, request, context, dspaceObject));
}
else
{
new DSpace().getEventService().fireEvent(
new UsageEvent(action, user_ip, user_agent, xforwarderfor, context, dspaceObject));
}
log.debug("fired event");
context.complete();
}
catch (SQLException e)
{
if ((context != null) && (context.isValid()))
{
context.abort();
}
log.error("Could not write usageEvent, SQLException. Message: " + e);
}
catch (ContextException e)
{
log.error("Could not write usageEvent, ContextException. Message: " + e.getMessage());
}
finally
{
if ((context != null) && (context.isValid()))
{
context.abort();
log.error("Something get wrong. Aborting context in finally statement.");
}
}
}
/**
* Process exception, print message to logger error stream and abort DSpace
* context.
*
* @param message
* Message, which will be printed to error stream.
* @param context
* Context which must be aborted.
* @throws WebApplicationException
* This exception is throw for user of REST api.
*/
protected static void processException(String message, org.dspace.core.Context context) throws WebApplicationException
{
if ((context != null) && (context.isValid()))
{
context.abort();
}
log.error(message);
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
/**
* Process finally statement. It will print message to logger error stream
* and abort DSpace context, if was not properly ended.
*
* @param context
* Context which must be aborted.
* @throws WebApplicationException
* This exception is throw for user of REST api.
*/
protected void processFinally(org.dspace.core.Context context) throws WebApplicationException
{
if ((context != null) && (context.isValid()))
{
context.abort();
log.error("Something get wrong. Aborting context in finally statement.");
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
/**
* Split string with regex ".".
*
* @param key
* String which will be splitted.
* @return String array filed with separated string.
*/
protected String[] mySplit(String key)
{
ArrayList<String> list = new ArrayList<String>();
int prev = 0;
for (int i = 0; i < key.length(); i++)
{
if (key.charAt(i) == '.')
{
list.add(key.substring(prev, i));
prev = i + 1;
}
else if (i + 1 == key.length())
{
list.add(key.substring(prev, i + 1));
}
}
if (list.size() == 2)
{
list.add(null);
}
return list.toArray(new String[0]);
}
/**
* Return string representation of values
* org.dspace.core.Constants.{READ,WRITE,DELETE}.
*
* @param action
* Constant from org.dspace.core.Constants.*
* @return String representation. read or write or delete.
*/
protected String getActionString(int action)
{
String actionStr;
switch (action)
{
case org.dspace.core.Constants.READ:
actionStr = "read";
break;
case org.dspace.core.Constants.WRITE:
actionStr = "write";
break;
case org.dspace.core.Constants.DELETE:
actionStr = "delete";
break;
case org.dspace.core.Constants.REMOVE:
actionStr = "remove";
break;
case org.dspace.core.Constants.ADD:
actionStr = "add";
break;
default:
actionStr = "(?action?)";
break;
}
return actionStr;
}
/**
* Return EPerson based on stored token in headers under
* "rest-dspace-token".
*
* @param headers
* Only must have "rest-api-token" for successfull return of
* user.
* @return Return EPerson logged under token in headers. If token was wrong
* or header rest-dspace-token was missing, returns null.
*/
protected static EPerson getUser(HttpHeaders headers)
{
List<String> list = headers.getRequestHeader(TokenHolder.TOKEN_HEADER);
String token = null;
if ((list != null) && (list.size() > 0))
{
token = list.get(0);
return TokenHolder.getEPerson(token);
}
return null;
}
protected static String getToken(HttpHeaders headers) {
List<String> list = headers.getRequestHeader(TokenHolder.TOKEN_HEADER);
String token = null;
if ((list != null) && (list.size() > 0))
{
token = list.get(0);
return token;
}
return null;
}
}

View File

@@ -7,39 +7,229 @@
*/
package org.dspace.rest;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/*
Root of API, should have documentation on where to find the other resources.
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.eperson.EPerson;
import org.dspace.rest.common.Status;
import org.dspace.rest.common.User;
import org.dspace.rest.exceptions.ContextException;
/**
* Root of RESTful api. It provides login and logout. Also have method for
* printing every method which is provides by RESTful api.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*
*/
@Path("/")
public class RestIndex {
private static Logger log = Logger.getLogger(RestIndex.class);
@javax.ws.rs.core.Context public static ServletContext servletContext;
/*
The "GET" annotation indicates this method will respond to HTTP Get requests.
The "Produces" annotation indicates the MIME response the method will return.
/**
* Return html page with information about REST api. It contains methods all
* methods provide by REST api.
*
* @return HTML page which has information about all methods of REST api.
*/
@GET
@Produces(MediaType.TEXT_HTML)
public String sayHtmlHello() {
return "<html><title>DSpace REST</title>" +
"<body><h1>DSpace REST API</h1>" +
// TODO Better graphics, add arguments to all methods. (limit, offset, item and so on)
return "<html><title>DSpace REST - index</title>" +
"<body>"
+ "<h1>DSpace REST API</h1>" +
"Server path: " + servletContext.getContextPath() +
"<h2>Index</h2>" +
"<ul>" +
"<li><a href='" + servletContext.getContextPath() + "/communities'>/communities</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/communities/1'>/communities/1</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/collections'>/collections</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/collections/1'>/collections/1</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/items'>/items</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/items/1'>/items/1</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/bitstreams'>/bitstreams</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/bitstreams/1'>/bitstreams/1</a></li>" +
"<li><a href='" + servletContext.getContextPath() + "/bitstreams/1/retrieve'>/bitstreams/1/retrieve</a></li>" +
"<li>GET / - It returns this page.</li>" +
"<li>GET /test - Return string \"REST api is running\". It is method for testing.</li>" +
"<li>POST /login - Method for login into DSpace RESTful api. You must post User class. Example: {\"email\":\"test@dspace\",\"password\":\"pass\"}. It returns token under which will must sending requests. In header \"rest-dspace-token\"</li>" +
"<li>POST /logout - Method for logout from DSpace RESTful api. You must post request with header \"rest-dspace-token\" token</li>" +
"</ul>" +
"<h2>Communities</h2>" +
"<ul>" +
"<li>GET /communities - Returns array of all communities in DSpace.</li>" +
"<li>GET /communities/top-communities - Returns array of all top communities in DSpace.</li>" +
"<li>GET /communities/{communityId} - Returns community.</li>" +
"<li>GET /communities/{communityId}/collections - Returns array of collections of community.</li>" +
"<li>GET /communities/{communityId}/communities - Returns array of subcommunities of community.</li>" +
"<li>POST /communities - Create new community at top level. You must post community.</li>" +
"<li>POST /communities/{communityId}/collections - Create new collections in community. You must post collection.</li>" +
"<li>POST /communities/{communityId}/communities - Create new subcommunity in community. You must post community.</li>" +
"<li>PUT /communities/{communityId} - Update community.</li>" +
"<li>DELETE /communities/{communityId} - Delete community.</li>" +
"<li>DELETE /communities/{communityId}/collections/{collectionId} - Delete collection in community.</li>" +
"<li>DELETE /communities/{communityId}/communities/{communityId2} - Delete subcommunity in community.</li>" +
"</ul>" +
"<h2>Collections</h2>" +
"<ul>" +
"<li>GET /collections - Return all collections of DSpace in array.</li>" +
"<li>GET /collections/{collectionId} - Return collection with id.</li>" +
"<li>GET /collections/{collectionId}/items - Return all items of collection.</li>" +
"<li>POST /collections/{collectionId}/items - Create posted item in collection.</li>" +
"<li>POST /collections/find-collection - Find collection by passed name.</li>" +
"<li>PUT /collections/{collectionId} </li> - Update collection. You muset post collection." +
"<li>DELETE /collections/{collectionId} - Delete collection from DSpace.</li>" +
"<li>DELETE /collections/{collectionId}/items/{itemId} - Delete item in collection. </li>" +
"</ul>" +
"<h2>Items</h2>" +
"<ul>" +
"<li>GET /items - Return list of items.</li>" +
"<li>GET /items/{item id} - Return item.</li>" +
"<li>GET /items/{item id}/metadata - Return item metadata.</li>" +
"<li>GET /items/{item id}/bitstreams - Return item bitstreams.</li>" +
"<li>POST /items/find-by-metadata-field - Find items by metadata entry.</li>" +
"<li>POST /items/{item id}/metadata - Add metadata to item.</li>" +
"<li>POST /items/{item id}/bitstreams - Add bitstream to item.</li>" +
"<li>PUT /items/{item id}/metadata - Update metadata in item.</li>" +
"<li>DELETE /items/{item id} - Delete item.</li>" +
"<li>DELETE /items/{item id}/metadata - Clear item metadata.</li>" +
"<li>DELETE /items/{item id}/bitstreams/{bitstream id} - Delete item bitstream.</li>" +
"</ul>" +
"<h2>Bitstreams</h2>" +
"<ul>" +
"<li>GET /bitstreams - Return all bitstreams in DSpace.</li>" +
"<li>GET /bitstreams/{bitstream id} - Return bitstream.</li>" +
"<li>GET /bitstreams/{bitstream id}/policy - Return bitstream policies.</li>" +
"<li>POST /bitstreams/{bitstream id}/retrieve - Return data of bitstream.</li>" +
"<li>POST /bitstreams/{bitstream id}/policy - Add policy to item.</li>" +
"<li>PUT /bitstreams/{bitstream id}/data - Update data of bitstream.</li>" +
"<li>PUT /bitstreams/{bitstream id} - Update metadata of bitstream.</li>" +
"<li>DELETE /bitstreams/{bitstream id} - Delete bitstream from DSpace.</li>" +
"<li>DELETE /bitstreams/{bitstream id}/policy/{policy_id} - Delete bitstream policy.</li>" +
"</ul>" +
"</body></html> ";
}
/**
* Method for only test if rest api is running.
*
* @return String "REST api is running."
*/
@GET
@Path("/test")
public String test()
{
return "REST api is running.";
}
/**
* Method for login user into REST api.
*
* @param user
* User which will be logged into REST api.
* @return Returns response code OK with token. Otherwise returns response
* code FORBIDDEN(403).
*/
@POST
@Path("/login")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response login(User user)
{
String token = TokenHolder.login(user);
if (token == null)
{
log.info("REST Login Attempt failed for user: " + user.getEmail());
return Response.status(Response.Status.FORBIDDEN).build();
} else {
log.info("REST Login Success for user: " + user.getEmail());
return Response.ok(token, "text/plain").build();
}
}
/**
* Method for logout from DSpace REST api. It removes token and user from
* TokenHolder.
*
* @param headers
* Request header which contains header with key
* "rest-dspace-token" and value of token.
* @return Return response OK, otherwise BAD_REQUEST, if was problem with
* logout or token is incorrect.
*/
@POST
@Path("/logout")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response logout(@Context HttpHeaders headers)
{
List<String> list = headers.getRequestHeader(TokenHolder.TOKEN_HEADER);
String token = null;
boolean logout = false;
EPerson ePerson = null;
if (list != null)
{
token = list.get(0);
ePerson = TokenHolder.getEPerson(token);
logout = TokenHolder.logout(token);
}
if ((token == null) || (!logout))
{
return Response.status(Response.Status.BAD_REQUEST).build();
}
if(ePerson != null) {
log.info("REST Logout: " + ePerson.getEmail());
}
return Response.ok().build();
}
/**
* ? status: OK
* authenticated: TRUE | FALSE
* epersonEMAIL: user@dspace.org
* epersonNAME: Joe User
* @param headers
* @return
*/
@GET
@Path("/status")
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Status status(@Context HttpHeaders headers) throws UnsupportedEncodingException {
org.dspace.core.Context context = null;
try {
context = Resource.createContext(Resource.getUser(headers));
EPerson ePerson = context.getCurrentUser();
if(ePerson != null) {
//DB EPerson needed since token won't have full info, need context
EPerson dbEPerson = EPerson.findByEmail(context, ePerson.getEmail());
String token = Resource.getToken(headers);
Status status = new Status(dbEPerson.getEmail(), dbEPerson.getFullName(), token);
return status;
}
} catch (ContextException e)
{
Resource.processException("Status context error: " + e.getMessage(), context);
} catch (SQLException e) {
Resource.processException("Status eperson db lookup error: " + e.getMessage(), context);
} catch (AuthorizeException e) {
Resource.processException("Status eperson authorize exception: " + e.getMessage(), context);
} finally {
context.abort();
}
//fallback status, unauth
return new Status();
}
}

View File

@@ -0,0 +1,154 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.eperson.EPerson;
import org.dspace.rest.common.User;
/**
* This class provide token generation, token holding and logging user into rest
* api. For login use method login with class org.dspace.rest.common.User. If
* you want to be deleted from holder, use method for logout.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*/
public class TokenHolder
{
private static final Logger log = Logger.getLogger(TokenHolder.class);
public static String TOKEN_HEADER = "rest-dspace-token";
private static Map<String, String> tokens = new HashMap<String, String>(); // Map with pair Email,token
private static Map<String, EPerson> persons = new HashMap<String, EPerson>(); // Map with pair token,Eperson
/**
* Login user into rest api. It check user credentials if they are okay.
*
* @param user
* User which will be logged into rest api.
* @return Returns generated token, which must be used in request header
* under rest-api-token. If password is bad or user does not exist,
* it returns NULL.
* @throws WebApplicationException
* It is thrown by SQLException if user could not be read from
* database. And by Authorization exception if context has not
* permission to read eperson.
*/
public static String login(User user) throws WebApplicationException
{
org.dspace.core.Context context = null;
String token = null;
try
{
context = new org.dspace.core.Context();
EPerson dspaceUser = EPerson.findByEmail(context, user.getEmail());
if ((dspaceUser == null) || (!dspaceUser.checkPassword(user.getPassword())))
{
token = null;
}
else if (tokens.containsKey(user.getEmail()))
{
token = tokens.get(user.getEmail());
}
else
{
token = generateToken();
persons.put(token, dspaceUser);
tokens.put(user.getEmail(), token);
}
log.trace("User(" + user.getEmail() + ") has been logged.");
context.complete();
}
catch (SQLException e)
{
context.abort();
log.error("Could not read user from database. Message:" + e);
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
catch (AuthorizeException e)
{
context.abort();
log.error("Could not find user, AuthorizeException. Message:" + e);
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
finally
{
if ((context != null) && (context.isValid()))
{
context.abort();
log.error("Something get wrong. Aborting context in finally statement.");
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
return token;
}
/**
* Return EPerson for log into context.
*
* @param token
* Token under which is stored eperson.
* @return Return instance of EPerson if is token right, otherwise it
* returns NULL.
*/
public static EPerson getEPerson(String token)
{
return persons.get(token);
}
/**
* Logout user from rest api. It delete token and EPerson from TokenHolder.
*
* @param token
* Token under which is stored eperson.
* @return Return true if was all okay, otherwise return false.
*/
public static boolean logout(String token)
{
if ((token == null) || (persons.get(token) == null))
{
return false;
}
String email = persons.get(token).getEmail();
EPerson person = persons.remove(token);
if (person == null)
{
return false;
}
tokens.remove(email);
return true;
}
/**
* It generates unique token.
*
* @return String filled with unique token.
*/
private static String generateToken()
{
return UUID.randomUUID().toString();
}
}

View File

@@ -7,15 +7,17 @@
*/
package org.dspace.rest.common;
import org.apache.log4j.Logger;
import org.dspace.core.Constants;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.log4j.Logger;
import org.dspace.content.Bundle;
import org.dspace.core.Constants;
/**
* Created with IntelliJ IDEA.
* User: peterdietz
@@ -37,6 +39,8 @@ public class Bitstream extends DSpaceObject {
private CheckSum checkSum;
private Integer sequenceId;
private ResourcePolicy[] policies = null;
public Bitstream() {
}
@@ -72,8 +76,22 @@ public class Bitstream extends DSpaceObject {
if(expandFields.contains("parent") || expandFields.contains("all")) {
parentObject = new DSpaceObject(bitstream.getParentObject());
} else if(expandFields.contains("policies") || expandFields.contains("all")) {
List<ResourcePolicy> tempPolicies = new ArrayList<ResourcePolicy>();
Bundle[] bundles = bitstream.getBundles();
for (Bundle bundle : bundles) {
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundle.getBitstreamPolicies();
for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies) {
if (policy.getResourceID() == this.getId()) {
tempPolicies.add(new ResourcePolicy(policy));
}
}
}
policies = tempPolicies.toArray(new ResourcePolicy[0]);
} else {
this.addExpand("parent");
this.addExpand("policies");
}
if(!expandFields.contains("all")) {
@@ -153,4 +171,13 @@ public class Bitstream extends DSpaceObject {
this.checkSum = checkSum;
}
public ResourcePolicy[] getPolicies() {
return policies;
}
public void setPolicies(ResourcePolicy[] policies) {
this.policies = policies;
}
}

View File

@@ -13,7 +13,6 @@ import org.dspace.content.ItemIterator;
import org.dspace.core.Context;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;

View File

@@ -38,8 +38,9 @@ public class Community extends DSpaceObject{
private String copyrightText, introductoryText, shortDescription, sidebarText;
private Integer countItems;
// Renamed because of xml annotation exception with this attribute and getSubCommunities.
@XmlElement(name = "subcommunities", required = true)
private List<Community> subCommunities = new ArrayList<Community>();
private List<Community> subcommunities = new ArrayList<Community>();
private List<Collection> collections = new ArrayList<Collection>();
@@ -87,10 +88,10 @@ public class Community extends DSpaceObject{
if(expandFields.contains("subCommunities") || expandFields.contains("all")) {
org.dspace.content.Community[] communityArray = community.getSubcommunities();
subCommunities = new ArrayList<Community>();
subcommunities = new ArrayList<Community>();
for(org.dspace.content.Community subCommunity : communityArray) {
if(AuthorizeManager.authorizeActionBoolean(context, subCommunity, org.dspace.core.Constants.READ)) {
subCommunities.add(new Community(subCommunity, null, context));
subcommunities.add(new Community(subCommunity, null, context));
} else {
log.info("Omitted restricted subCommunity: " + subCommunity.getID() + " _ " + subCommunity.getName());
}
@@ -171,4 +172,13 @@ public class Community extends DSpaceObject{
public Bitstream getLogo() {
return logo;
}
public List<Community> getSubcommunities() {
return subcommunities;
}
public void setSubcommunities(List<Community> subcommunities) {
this.subcommunities = subcommunities;
}
}

View File

@@ -11,6 +11,7 @@ import org.atteo.evo.inflector.English;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;
@@ -26,9 +27,7 @@ public class DSpaceObject {
private Integer id;
private String name;
private String handle;
private String type;
@XmlElement(name = "link", required = true)
@@ -42,17 +41,17 @@ public class DSpaceObject {
}
public DSpaceObject(org.dspace.content.DSpaceObject dso) {
setID(dso.getID());
setId(dso.getID());
setName(dso.getName());
setHandle(dso.getHandle());
setType(dso.getTypeText().toLowerCase());
}
public Integer getID() {
public Integer getId() {
return id;
}
public void setID(Integer id) {
public void setId(Integer id) {
this.id = id;
}
@@ -74,7 +73,7 @@ public class DSpaceObject {
public String getLink() {
//TODO, get actual contextPath of /rest/
return "/rest/" + English.plural(getType()) + "/" + getID();
return "/RESTapi/" + English.plural(getType()) + "/" + getId();
}
public String getType() {

View File

@@ -17,6 +17,7 @@ import org.dspace.core.Context;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,6 +30,7 @@ import java.util.List;
* Time: 4:50 PM
* To change this template use File | Settings | File Templates.
*/
@SuppressWarnings("deprecation")
@XmlRootElement(name = "item")
public class Item extends DSpaceObject {
Logger log = Logger.getLogger(Item.class);
@@ -39,11 +41,8 @@ public class Item extends DSpaceObject {
Collection parentCollection;
List<Collection> parentCollectionList;
List<Community> parentCommunityList;
List<MetadataEntry> metadata;
List<Bitstream> bitstreams;
public Item(){}
@@ -63,9 +62,8 @@ public class Item extends DSpaceObject {
metadata = new ArrayList<MetadataEntry>();
DCValue[] dcvs = item.getMetadata(org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY);
for (DCValue dcv : dcvs) {
if (!MetadataExposure.isHidden(context, dcv.schema, dcv.element, dcv.qualifier)) {
metadata.add(new MetadataEntry(dcv.getField(), dcv.value));
}
if (!MetadataExposure.isHidden(context, dcv.schema, dcv.element, dcv.qualifier))
metadata.add(new MetadataEntry(dcv.getField(), dcv.value, dcv.language));
}
} else {
this.addExpand("metadata");

View File

@@ -10,37 +10,58 @@ package org.dspace.rest.common;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Created with IntelliJ IDEA.
* User: peterdietz
* Date: 9/20/13
* Time: 5:51 PM
* To change this template use File | Settings | File Templates.
* @author peterdietz, Rostislav Novak (Computing and Information Centre, CTU in
* Prague)
*
*/
@XmlRootElement(name = "metadataentry")
public class MetadataEntry {
public class MetadataEntry
{
String key;
String value;
public MetadataEntry() {}
String language;
public MetadataEntry(String key, String value) {
this.key = key;
this.value = value;
public MetadataEntry()
{
}
public String getValue() {
public MetadataEntry(String key, String value, String language)
{
this.key = key;
this.value = value;
this.language = language;
}
public String getValue()
{
return value;
}
public void setValue(String value) {
public void setValue(String value)
{
this.value = value;
}
public String getKey() {
public String getKey()
{
return key;
}
public void setKey(String key) {
public void setKey(String key)
{
this.key = key;
}
public String getLanguage()
{
return language;
}
public void setLanguage(String language)
{
this.language = language;
}
}

View File

@@ -0,0 +1,181 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.common;
import java.util.Date;
import org.codehaus.jackson.annotate.JsonIgnore;
public class ResourcePolicy{
public enum Action {
READ, WRITE, DELETE;
}
private Integer id;
private Action action;
private Integer epersonId;
private Integer groupId;
private Integer resourceId;
private String resourceType;
private String rpDescription;
private String rpName;
private String rpType;
private Date startDate;
private Date endDate;
public ResourcePolicy() {}
public ResourcePolicy(org.dspace.authorize.ResourcePolicy dspacePolicy) {
this.id = dspacePolicy.getID();
switch(dspacePolicy.getAction()) {
case org.dspace.core.Constants.READ:
this.action = Action.READ;
break;
case org.dspace.core.Constants.WRITE:
this.action = Action.WRITE;
break;
case org.dspace.core.Constants.DELETE:
this.action = Action.DELETE;
break;
}
this.epersonId = dspacePolicy.getEPersonID();
this.groupId = dspacePolicy.getGroupID();
this.resourceId = dspacePolicy.getResourceID();
this.rpDescription = dspacePolicy.getRpDescription();
this.rpName = dspacePolicy.getRpName();
this.rpType = dspacePolicy.getRpType();
this.startDate = dspacePolicy.getStartDate();
this.endDate = dspacePolicy.getEndDate();
switch(dspacePolicy.getResourceType()) {
case org.dspace.core.Constants.BITSTREAM:
this.resourceType = "bitstream";
break;
case org.dspace.core.Constants.ITEM:
this.resourceType = "item";
break;
case org.dspace.core.Constants.COLLECTION:
this.resourceType = "collection";
break;
case org.dspace.core.Constants.COMMUNITY:
this.resourceType = "community";
break;
case org.dspace.core.Constants.BUNDLE:
this.resourceType = "bundle";
break;
default:
this.resourceType = "";
break;
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Action getAction() {
return action;
}
@JsonIgnore
public int getActionInt(){
switch(action) {
case READ:
return org.dspace.core.Constants.READ;
case WRITE:
return org.dspace.core.Constants.WRITE;
case DELETE:
return org.dspace.core.Constants.DELETE;
}
return org.dspace.core.Constants.READ;
}
public void setAction(Action action) {
this.action = action;
}
public Integer getEpersonId() {
return epersonId;
}
public void setEpersonId(Integer epersonId) {
this.epersonId = epersonId;
}
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public Integer getResourceId() {
return resourceId;
}
public void setResourceId(Integer resourceId) {
this.resourceId = resourceId;
}
public String getResourceType() {
return resourceType;
}
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
public String getRpDescription() {
return rpDescription;
}
public void setRpDescription(String rpDescription) {
this.rpDescription = rpDescription;
}
public String getRpName() {
return rpName;
}
public void setRpName(String rpName) {
this.rpName = rpName;
}
public String getRpType() {
return rpType;
}
public void setRpType(String rpType) {
this.rpType = rpType;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
}

View File

@@ -0,0 +1,105 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.common;
import org.codehaus.jackson.annotate.JsonProperty;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Used to handle/determine status of REST API.
* Mainly to know your authentication status
*
*/
@XmlRootElement(name = "status")
public class Status
{
private boolean okay;
private boolean authenticated;
private String email;
private String fullname;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
private String token;
public Status() {
setOkay(true);
setAuthenticated(false);
}
public Status(String email, String fullname, String token) {
setOkay(true);
setAuthenticated(true);
setEmail(email);
setFullname(fullname);
setToken(token);
}
public Status(EPerson eperson, String token) {
setOkay(true);
if(eperson != null) {
setAuthenticated(true);
setEmail(eperson.getEmail());
setFullname(eperson.getFullName());
setToken(token);
} else {
setAuthenticated(false);
}
}
@JsonProperty("okay")
public boolean isOkay()
{
return this.okay;
}
@JsonProperty("okay")
public void setOkay(boolean okay)
{
this.okay = okay;
}
@JsonProperty("authenticated")
public boolean isAuthenticated() {
return authenticated;
}
@JsonProperty("authenticated")
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
@JsonProperty("email")
public String getEmail() {
return email;
}
@JsonProperty("email")
public void setEmail(String email) {
this.email = email;
}
@JsonProperty("fullname")
public String getFullname() {
return fullname;
}
@JsonProperty("fullname")
public void setFullname(String fullname) {
this.fullname = fullname;
}
}

View File

@@ -0,0 +1,56 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.common;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Class for handle login information for POST request.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*
*/
@XmlRootElement(name = "user")
public class User
{
private String email;
private String password;
public User()
{
}
public User(String email, String password)
{
this.email = email;
this.password = password;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}

View File

@@ -0,0 +1,35 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.exceptions;
/**
* Simple exception which only encapsulate classic exception. This exception is
* only for exceptions caused by creating context.
*
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
*
*/
public class ContextException extends Exception
{
private static final long serialVersionUID = 1L;
Exception causedBy;
public ContextException(String message, Exception causedBy)
{
super(message);
this.causedBy = causedBy;
}
public Exception getCausedBy()
{
return causedBy;
}
}

View File

@@ -36,23 +36,23 @@
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DSpace REST API</servlet-name>
<!--
The url-pattern can be used to define your URL.
Example, running local host with a context of "hello" and path annotation of "world"
on the HelloWorld class:
<url-pattern>/*</url-pattern>
The web service will be available at: http://localhost:8080/hello/world
<url-pattern>/jersey/*</url-pattern>
The web service will be available at http://localhost:8080/hello/jersey/world
-->
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!-- Security settings and mapping -->
<security-constraint>
<web-resource-collection>
<web-resource-name>DSpace REST API</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- DSpace Configuration Information -->
<context-param>
<param-name>dspace-config</param-name>
@@ -98,6 +98,4 @@
</listener-class>
</listener>
</web-app>