Merge pull request #1679 from DSpace/rest7

Merge the Rest7 branch back to the master
This commit is contained in:
Tim Donohue
2017-03-23 11:38:27 -05:00
committed by GitHub
71 changed files with 3391 additions and 58 deletions

View File

@@ -9,7 +9,6 @@ jdk:
# DS-3384 Oracle JDK 8 has DocLint enabled by default.
# Let's use this to catch any newly introduced DocLint issues.
- oraclejdk8
- oraclejdk7
## Should we run into any problems with oraclejdk8 on Travis, we may try the following workaround.
## https://docs.travis-ci.com/user/languages/java#Testing-Against-Multiple-JDKs

View File

@@ -434,10 +434,6 @@
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>

View File

@@ -95,6 +95,12 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl<Bitstream> imp
return bitstreamDAO.findAll(context, Bitstream.class);
}
@Override
public Iterator<Bitstream> findAll(Context context, int limit, int offset) throws SQLException
{
return bitstreamDAO.findAll(context, limit, offset);
}
@Override
public Bitstream create(Context context, InputStream is) throws IOException, SQLException {
// Store the bits

View File

@@ -147,7 +147,7 @@ public abstract class DSpaceObject implements Serializable, ReloadableEntity<jav
return handles;
}
protected List<MetadataValue> getMetadata() {
public List<MetadataValue> getMetadata() {
return metadata;
}

View File

@@ -200,6 +200,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
return itemDAO.findAll(context, true);
}
@Override
public Iterator<Item> findAll(Context context, Integer limit, Integer offset) throws SQLException {
return itemDAO.findAll(context, true, limit, offset);
}
@Override
public Iterator<Item> findAllUnfiltered(Context context) throws SQLException {
return itemDAO.findAll(context, true, true);

View File

@@ -26,6 +26,8 @@ import java.util.List;
*/
public interface BitstreamDAO extends DSpaceObjectLegacySupportDAO<Bitstream> {
public Iterator<Bitstream> findAll(Context context, int limit, int offset) throws SQLException;
public List<Bitstream> findDeletedBitstreams(Context context) throws SQLException;
public List<Bitstream> findDuplicateInternalIdentifier(Context context, Bitstream bitstream) throws SQLException;

View File

@@ -30,6 +30,8 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item>
{
public Iterator<Item> findAll(Context context, boolean archived) throws SQLException;
public Iterator<Item> findAll(Context context, boolean archived, int limit, int offset) throws SQLException;
public Iterator<Item> findAll(Context context, boolean archived, boolean withdrawn) throws SQLException;
/**

View File

@@ -153,4 +153,12 @@ public class BitstreamDAOImpl extends AbstractHibernateDSODAO<Bitstream> impleme
" and bit.id not in (select col.logo.id from Collection col)" +
" and bit.id not in (select bun.primaryBitstream.id from Bundle bun)"));
}
@Override
public Iterator<Bitstream> findAll(Context context, int limit, int offset) throws SQLException {
Query query = createQuery(context, "select b FROM Bitstream b");
query.setFirstResult(offset);
query.setMaxResults(limit);
return iterate(query);
}
}

View File

@@ -55,6 +55,16 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
return iterate(query);
}
@Override
public Iterator<Item> findAll(Context context, boolean archived, int limit, int offset) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive");
query.setParameter("in_archive", archived);
query.setFirstResult(offset);
query.setMaxResults(limit);
return iterate(query);
}
@Override
public Iterator<Item> findAll(Context context, boolean archived, boolean withdrawn) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive or withdrawn = :withdrawn");

View File

@@ -27,6 +27,8 @@ public interface BitstreamService extends DSpaceObjectService<Bitstream>, DSpace
public List<Bitstream> findAll(Context context) throws SQLException;
public Iterator<Bitstream> findAll(Context context, int limit, int offset) throws SQLException;
/**
* Create a new bitstream, with a new ID. The checksum and file size are
* calculated. No authorization checks are made in this method.

View File

@@ -69,6 +69,18 @@ public interface ItemService extends DSpaceObjectService<Item>, DSpaceObjectLega
*/
public Iterator<Item> findAll(Context context) throws SQLException;
/**
* Get all the items in the archive. Only items with the "in archive" flag
* set are included. The order of the list is indeterminate.
*
* @param context DSpace context object
* @param limit limit
* @param offset offset
* @return an iterator over the items in the archive.
* @throws SQLException if database error
*/
public Iterator<Item> findAll(Context context, Integer limit, Integer offset) throws SQLException;
/**
* Get all "final" items in the archive, both archived ("in archive" flag) or
* withdrawn items are included. The order of the list is indeterminate.

View File

@@ -142,7 +142,8 @@ public class Context
if(dbConnection == null)
{
// Obtain a non-auto-committing connection
dbConnection = new DSpace().getSingletonService(DBConnection.class);
dbConnection = new DSpace().getServiceManager()
.getServiceByName(null, DBConnection.class);
if(dbConnection == null)
{
log.fatal("Cannot obtain the bean which provides a database connection. " +

View File

@@ -7,7 +7,13 @@
*/
package org.dspace.core;
import org.dspace.content.DSpaceObject;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.dspace.storage.rdbms.DatabaseConfigVO;
import org.hibernate.FlushMode;
import org.hibernate.Session;
@@ -15,15 +21,10 @@ import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.proxy.HibernateProxyHelper;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import javax.sql.DataSource;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
/**
* Hibernate implementation of the DBConnection
@@ -50,7 +51,7 @@ public class HibernateDBConnection implements DBConnection<Session> {
@Override
public boolean isTransActionAlive() {
Transaction transaction = getTransaction();
return transaction != null && transaction.isActive();
return transaction != null && transaction.getStatus().isOneOf(TransactionStatus.ACTIVE);
}
protected Transaction getTransaction() {
@@ -59,7 +60,7 @@ public class HibernateDBConnection implements DBConnection<Session> {
@Override
public boolean isSessionAlive() {
return sessionFactory.getCurrentSession() != null && sessionFactory.getCurrentSession().getTransaction() != null && sessionFactory.getCurrentSession().getTransaction().isActive();
return sessionFactory.getCurrentSession() != null && sessionFactory.getCurrentSession().getTransaction() != null && sessionFactory.getCurrentSession().getTransaction().getStatus().isOneOf(TransactionStatus.ACTIVE);
}
@Override
@@ -79,7 +80,7 @@ public class HibernateDBConnection implements DBConnection<Session> {
@Override
public void commit() throws SQLException {
if(isTransActionAlive() && !getTransaction().wasRolledBack())
if(isTransActionAlive() && !getTransaction().getStatus().isOneOf(TransactionStatus.MARKED_ROLLBACK, TransactionStatus.ROLLING_BACK))
{
getSession().flush();
getTransaction().commit();

View File

@@ -10,22 +10,23 @@ package org.dspace.handle.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.core.AbstractHibernateDAO;
import org.dspace.core.Context;
import org.dspace.handle.Handle;
import org.dspace.handle.dao.HandleDAO;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.service.jdbc.dialect.internal.StandardDialectResolver;
import org.hibernate.service.jdbc.dialect.spi.DialectResolver;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
/**
* Hibernate implementation of the Database Access Object interface class for the Handle object.
@@ -123,7 +124,7 @@ public class HandleDAOImpl extends AbstractHibernateDAO<Handle> implements Handl
// Determine what dialect we are using for this DB
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
Dialect dialect = dialectResolver.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData()));
// Find the next value in our sequence (based on DB dialect)
try (PreparedStatement preparedStatement = connection.prepareStatement(dialect.getSequenceNextValString(HANDLE_SEQUENCE)))

View File

@@ -7,15 +7,14 @@
*/
package org.dspace.storage.rdbms.hibernate.postgres;
import java.sql.Types;
import org.hibernate.dialect.PostgreSQL82Dialect;
import org.hibernate.metamodel.spi.TypeContributions;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.PostgresUUIDType;
import org.hibernate.type.descriptor.sql.LongVarcharTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import java.sql.Types;
/**
* UUID's are not supported by default in hibernate due to differences in the database in order to fix this a custom sql dialect is needed.
* Source: https://forum.hibernate.org/viewtopic.php?f=1&amp;t=1014157
@@ -25,7 +24,7 @@ import java.sql.Types;
public class DSpacePostgreSQL82Dialect extends PostgreSQL82Dialect
{
@Override
public void contributeTypes(final TypeContributions typeContributions, final ServiceRegistry serviceRegistry) {
public void contributeTypes(final org.hibernate.boot.model.TypeContributions typeContributions, final ServiceRegistry serviceRegistry) {
super.contributeTypes(typeContributions, serviceRegistry);
typeContributions.contributeType(new InternalPostgresUUIDType());
}

View File

@@ -1,13 +0,0 @@
/**
* 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/
*/
/**
* Tests for the Checksum Checker tool, which looks for unexpected alterations
* in Bitstream content.
*/
package org.dspace.checker.dao.impl;

View File

@@ -100,7 +100,7 @@
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.3</version>
<version>2.6.11</version>
<scope>compile</scope>
</dependency>
<!-- for filters -->
@@ -165,6 +165,11 @@
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<developers>

View File

@@ -61,4 +61,8 @@ public final class DSpaceBeanPostProcessor implements BeanPostProcessor, Destruc
DSpaceServiceManager.shutdownService(bean);
}
// @Override
public boolean requiresDestruction(Object arg0) {
return false;
}
}

View File

@@ -9,6 +9,7 @@ package org.dspace.utils.servlet;
import java.io.IOException;
import javax.annotation.Priority;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -31,6 +32,7 @@ import org.dspace.services.RequestService;
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
@Priority(1)
public final class DSpaceWebappServletFilter implements Filter {
/* (non-Javadoc)

View File

@@ -49,6 +49,7 @@
</constructor-arg>
</bean>
</property>
<property name="shared" value="true"/>
</bean>
<!-- CACHING end beans -->

View File

@@ -0,0 +1,43 @@
# DSpace7 REST Webapp
> This is the new REST webapp for DSpace 7 build with Spring MVC + HATEOAS with a focus on the [JSON HAL format](http://stateless.co/hal_specification.html) ([formal specification](https://tools.ietf.org/html/draft-kelly-json-hal-08))
This webapp uses the following technologies:
- [Spring Boot](https://projects.spring.io/spring-boot/)
- [Spring MVC](https://spring.io/guides/gs/rest-service/)
- [Spring HATEOAS](http://projects.spring.io/spring-hateoas/)
*Please note that we don't use Spring Data REST* but we mimic as much as possible its architecture and behaviour.
We don't use Spring Data REST as we haven't a spring data layer and we want to provide clear separation between the persistence representation and the REST representation
## How to contribute
Check the infomation available on the DSpace Official Wiki page for the [DSpace 7 Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group)
[DSpace 7 REST: Coding DSpace Objects](https://wiki.duraspace.org/display/DSPACE/DSpace+7+REST%3A+Coding+DSpace+Objects)
## How to run
The only tested way right now is to run this webapp inside your IDE (Eclipse). Just create a new Tomcat 8 server and deploy the dspace-spring-rest maven module to it.
> The *dspace.dir* is configured in the *dspace-spring-rest/src/main/resources/application.properties* file
[currently](src/main/resources/application.properties#L25)
> dspace.dir = d:/install/dspace7
## HAL Browser
The modified version of the HAL Browser from the Spring Data REST project is included, the index.html file is overriden locally to support the /api baseURL (see [DATAREST-971](https://jira.spring.io/browse/DATAREST-971))
## Packages and main classes
*[org.dspace.app.rest.Application](src/main/java/org/dspace/app/rest/Application.java)* is the spring boot main class it initializes
- the DSpace kernel
- the dspaceContextListener
- the DSpaceWebappServletFilter
- the dspaceRequestContextFilter
- a custom HAL RelProvider for dspace
*[org.dspace.app.rest.RestResourceController](src/main/java/org/dspace/app/rest/RestResourceController.java)* is the controller responsible to handle all the REST requests (so far) delegating the execution to the right repository depending on the requested resource (REST entity)
*[org.dspace.app.rest.model](src/main/java/org/dspace/app/rest/model)* is the package where to put all the classes representing DSpace REST resource. The classes should be named with the name of the DSpace perstent class mainly exposed + the Rest suffix (i.e. ItemRest, CollectionRest, etc.)
*[org.dspace.app.rest.model.hateoas](src/main/java/org/dspace/app/rest/model/hateoas)* contains the classes specific of the HAL implementation. The most important class is org.dspace.app.rest.model.hateoas.DSpaceResource<T> that wrap a DSpaceRest object adding the support for the links and embedded resources
*[org.dspace.app.rest.repository](src/main/java/org/dspace/app/rest/repository)* contains the implementation of the Repository Design pattern for the Rest Objects
*[org.dspace.app.rest.converter](src/main/java/org/dspace/app/rest/converter)* contains the converters from/to DSpace persistent entities (Item, Collection, etc.) to their equivalent REST object (ItemRest, CollectionRest, etc.)

143
dspace-spring-rest/pom.xml Normal file
View File

@@ -0,0 +1,143 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-spring-rest</artifactId>
<packaging>war</packaging>
<name>DSpace Spring Rest (Boot MVC + HATEOAS)</name>
<description>
DSpace new Rest API
</description>
<!--
A Parent POM that Maven inherits DSpace Default
POM attributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<!-- This is the path to the root [dspace-src] directory. -->
<root.basedir>${basedir}/..</root.basedir>
<!-- Default resource delimiter for Spring Boot, so it doesn't clash with Spring ${} placeholders-->
<resource.delimiter>@</resource.delimiter>
<!-- Define our starting class for our Spring Boot Application -->
<start-class>org.dspace.app.rest.Application</start-class>
<spring-boot.version>1.4.4.RELEASE</spring-boot.version>
<json-path.version>2.2.0</json-path.version>
<!-- <springdata.commons>1.13.0.RELEASE</springdata.commons> -->
</properties>
<dependencies>
<!-- These next two dependencies build a WAR that is BOTH executable
AND deployable into an external container (Tomcat).
See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-maven-packaging -->
<!-- NOTE: For rapid development (if you don't need Solr or other webapps),
you can temporarily comment these out, and switch <packaging> to "jar".
This lets you develop in a standalone, runnable JAR application. -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>-->
<!-- Ensure embedded servlet container doesn't interfere when this
WAR is deployed to an external Tomcat (i.e. provided). -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
<version>${json-path.version}</version>
</dependency> -->
<!-- The HAL Browser -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
<version>2.5.6.RELEASE</version>
<!-- if you get a java.util.zip.ZipException: invalid LOC header (bad signature)
during the tomcat startup force the use of the previous version as the jar file
looks corrupted in the maven repository -->
<!-- <version>2.5.5.RELEASE</version> -->
</dependency>
<!-- Add in Spring Security for AuthN and AuthZ -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> -->
<!-- Add in log4j support by excluding default logging, and using starter-log4j -->
<!-- See: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html#howto-configure-log4j-for-logging -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>-->
<!-- DSpace dependencies -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-services</artifactId>
</dependency>
<!-- Apache Commons Collections 4.1 (used for MultiValuedMap to store metadata values for views) -->
<!-- NOTE: Currently DSpace API / Services still used 3.2 and may need upgrading at some point -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,175 @@
/**
* 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.app.rest;
import java.io.File;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.dspace.app.rest.filter.DSpaceRequestContextFilter;
import org.dspace.app.rest.model.hateoas.DSpaceRelProvider;
import org.dspace.app.rest.utils.ApplicationConfig;
import org.dspace.app.util.DSpaceContextListener;
import org.dspace.servicemanager.DSpaceKernelImpl;
import org.dspace.servicemanager.DSpaceKernelInit;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.dspace.utils.servlet.DSpaceWebappServletFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.hateoas.RelProvider;
/**
* Define the Spring Boot Application settings itself. This class takes the place
* of a web.xml file, and configures all Filters/Listeners as methods (see below).
* <P>
* NOTE: Requires a Servlet 3.0 container, e.g. Tomcat 7.0 or above.
* <p>
* NOTE: This extends SpringBootServletInitializer in order to allow us to build
* a deployable WAR file with Spring Boot. See:
* http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Tim Donohue
*/
@SpringBootApplication
public class Application extends SpringBootServletInitializer
{
private static final Logger log = LoggerFactory.getLogger(Application.class);
@Autowired
private ApplicationConfig configuration;
/**
* Override the default SpringBootServletInitializer.configure() method,
* passing it this Application class.
* <P>
* This is necessary to allow us to build a deployable WAR, rather than
* always relying on embedded Tomcat.
* <P>
* See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file
* @param application
* @return
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
@Bean
public ServletContextInitializer contextInitializer() {
return new ServletContextInitializer() {
private transient DSpaceKernelImpl kernelImpl;
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
servletContext.setInitParameter("dspace.dir",configuration.getDspaceHome());
// start the kernel when the webapp starts
try {
this.kernelImpl = DSpaceKernelInit.getKernel(null);
if (! this.kernelImpl.isRunning()) {
this.kernelImpl.start(getProvidedHome(configuration.getDspaceHome())); // init the kernel
}
} catch (Exception e) {
// failed to start so destroy it and log and throw an exception
try {
this.kernelImpl.destroy();
} catch (Exception e1) {
// nothing
}
String message = "Failure during filter init: " + e.getMessage();
System.err.println(message + ":" + e);
throw new RuntimeException(message, e);
}
}
/*
* Find DSpace's "home" directory.
* Initially look for JNDI Resource called "java:/comp/env/dspace.dir".
* If not found, look for "dspace.dir" initial context parameter.
*/
private String getProvidedHome(String dspaceHome){
String providedHome = null;
try {
Context ctx = new InitialContext();
providedHome = (String) ctx.lookup("java:/comp/env/" + DSpaceConfigurationService.DSPACE_HOME);
} catch (Exception e) {
// do nothing
}
if (providedHome == null)
{
if(dspaceHome != null && !dspaceHome.equals("") &&
!dspaceHome.equals("${" + DSpaceConfigurationService.DSPACE_HOME + "}")){
File test = new File(dspaceHome);
if(test.exists() && new File(test,DSpaceConfigurationService.DSPACE_CONFIG_PATH).exists()) {
providedHome = dspaceHome;
}
}
}
return providedHome;
}
};
}
/**
* Register the "DSpaceContextListener" so that it is loaded
* for this Application.
* @return DSpaceContextListener
*/
@Bean
@Order(2)
protected DSpaceContextListener dspaceContextListener() {
// This listener initializes the DSpace Context object
// (and loads all DSpace configs)
return new DSpaceContextListener();
}
/**
* Register the DSpaceWebappServletFilter, which initializes the
* DSpace RequestService / SessionService
*
* @return DSpaceWebappServletFilter
*/
@Bean
@Order(1)
protected Filter dspaceWebappServletFilter() {
return new DSpaceWebappServletFilter();
}
/**
* Register the DSpaceRequestContextFilter, a Filter which checks for open
* Context objects *after* a request has been fully processed, and closes them
*
* @return DSpaceRequestContextFilter
*/
@Bean
@Order(2)
protected Filter dspaceRequestContextFilter() {
return new DSpaceRequestContextFilter();
}
@Bean
protected RelProvider dspaceRelProvider() {
return new DSpaceRelProvider();
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.app.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Service;
/**
* A Service able to list all the discoverable endpoints in our REST
* application. Endpoints need to register their managed endpoints. The service
* is responsible to check conflict and priorities
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Tim Donohue
*/
@Service
public class DiscoverableEndpointsService {
/**
* Contains all the registeredEndpoints as received by the single controller
*/
private Map<Object, List<Link>> registeredEndpoints = new HashMap<Object, List<Link>>();
/**
* Is the computed list of discoverableEndpoints valid?
*/
private boolean initialized = false;
private List<Link> discoverableEndpoints = new ArrayList<Link>();
public void register(Object controller, List<Link> links) {
synchronized (this) {
initialized = false;
registeredEndpoints.put(controller, links);
}
}
public void unregister(Object controller) {
synchronized (this) {
initialized = false;
registeredEndpoints.remove(controller);
}
}
public List<Link> getDiscoverableEndpoints() {
synchronized (this) {
if (initialized)
return discoverableEndpoints;
discoverableEndpoints.clear();
Set<String> rels = new HashSet<String>();
for (Entry<Object, List<Link>> controller : registeredEndpoints.entrySet()) {
for (Link link : controller.getValue()) {
if (isLinkValid(controller.getKey(), link.getHref())) {
discoverableEndpoints.add(link);
// sanity check
// FIXME improve logging for debugging
if (rels.contains(link.getRel())) {
throw new IllegalStateException("The rel " + link.getRel() + " is defined multiple times!");
}
rels.add(link.getRel());
}
}
}
initialized = true;
return discoverableEndpoints;
}
}
private boolean isLinkValid(Object controller, String href) {
// FIXME we need to implement a check to be sure that there are no other
// controller with an highter precedence mapped on the same URL (this
// could be used to override default implementation)
return true;
}
}

View File

@@ -0,0 +1,147 @@
/**
* 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.app.rest;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.exception.PaginationException;
import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.utils.Utils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* This is the main entry point of the new REST API. Its responsibility is to
* provide a consistent behaviors for all the exposed resources in terms of
* returned HTTP codes, endpoint URLs, HTTP verbs to methods translation, etc.
* It delegates to the repository the business logic
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RestController
@RequestMapping("/api/core/{model}")
@SuppressWarnings("rawtypes")
public class RestResourceController implements InitializingBean {
@Autowired
DiscoverableEndpointsService discoverableEndpointsService;
@Autowired
Utils utils;
@Override
public void afterPropertiesSet() {
List<Link> links = new ArrayList<Link>();
for (String r : utils.getRepositories()) {
// this doesn't work as we don't have an active http request
// see https://github.com/spring-projects/spring-hateoas/issues/408
// Link l = linkTo(this.getClass(), r).withRel(r);
String plural = English.plural(r);
Link l = new Link("/api/core/" + plural, plural);
links.add(l);
System.out.println(l.getRel() + " " + l.getHref());
}
discoverableEndpointsService.register(this, links);
}
@RequestMapping(method = RequestMethod.GET, value = "/{id:\\d+}")
@SuppressWarnings("unchecked")
DSpaceResource<RestModel> findOne(@PathVariable String model, @PathVariable Integer id, @RequestParam(required=false) String projection) {
return findOneInternal(model, id, projection);
}
@RequestMapping(method = RequestMethod.GET, value = "/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}")
@SuppressWarnings("unchecked")
DSpaceResource<RestModel> findOne(@PathVariable String model, @PathVariable UUID uuid, @RequestParam(required=false) String projection) {
return findOneInternal(model, uuid, projection);
}
private <ID extends Serializable> DSpaceResource<RestModel> findOneInternal(String model, ID id, String projection) {
DSpaceRestRepository<RestModel, ID> repository = utils.getResourceRepository(model);
RestModel modelObject = null;
try {
modelObject = repository.findOne(id);
} catch (ClassCastException e) {
}
if (modelObject == null) {
throw new ResourceNotFoundException(model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
return result;
}
@RequestMapping(method = RequestMethod.GET, value = "/{id:\\d+}/{rel}")
ResourceSupport findRel(@PathVariable String model, @PathVariable Integer id, @PathVariable String rel, @RequestParam(required=false) String projection) {
return findRelInternal(model, id, rel, projection);
}
@RequestMapping(method = RequestMethod.GET, value = "/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}/{rel}")
ResourceSupport findRel(@PathVariable String model, @PathVariable UUID uuid, @PathVariable String rel, @RequestParam(required=false) String projection) {
return findRelInternal(model, uuid, rel, projection);
}
private <ID extends Serializable> ResourceSupport findRelInternal(String model, ID uuid, String rel, String projection) {
// FIXME this is a very bad implementation as it leads most of times to
// more round-trip on the database and retrieval of unneeded infromation
DSpaceRestRepository<RestModel, ID> repository = utils.getResourceRepository(model);
RestModel modelObject = repository.findOne(uuid);
DSpaceResource result = repository.wrapResource(modelObject, rel);
if (result.getLink(rel) == null) {
//TODO create a custom exception
throw new ResourceNotFoundException(rel + "undefined for "+ model);
}
ResourceSupport resu = (ResourceSupport) result.getEmbedded().get(rel);
return resu;
}
@RequestMapping(method = RequestMethod.GET)
@SuppressWarnings("unchecked")
<T extends RestModel> PagedResources<DSpaceResource<T>> findAll(@PathVariable String model, Pageable page, PagedResourcesAssembler assembler, @RequestParam(required=false) String projection) {
DSpaceRestRepository<T, ?> repository = utils.getResourceRepository(model);
// Link link = entityLinks.linkFor(getResourceClass(model), model, page).withSelfRel();
Link link = linkTo(this.getClass(), model).withSelfRel();
Page<DSpaceResource<T>> resources;
try {
resources = repository.findAll(page).map(repository::wrapResource);
// resources.forEach(r -> {
// Link linkToSingleResource = Utils.linkToSingleResource(r, Link.REL_SELF);
// r.add(linkToSingleResource);
// });
} catch (PaginationException pe) {
resources = new PageImpl<DSpaceResource<T>>(new ArrayList<DSpaceResource<T>>(), page, pe.getTotal());
}
PagedResources<DSpaceResource<T>> result = assembler.toResource(resources, link);
return result;
}
}

View File

@@ -0,0 +1,42 @@
/**
* 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.app.rest;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This is the main entry point of the new REST API. Its responsibility is to
* provide a consistent behaviors for all the exposed resources in terms of
* returned HTTP codes, endpoint URLs, HTTP verbs to methods translation, etc.
* It delegates to the repository the business logic
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RestController
@RequestMapping("/api")
public class RootRestResourceController {
@Autowired
DiscoverableEndpointsService discoverableEndpointsService;
@RequestMapping(method = RequestMethod.GET)
ResourceSupport listDefinedEndpoint(HttpServletRequest request) {
ResourceSupport root = new ResourceSupport();
for (Link l : discoverableEndpointsService.getDiscoverableEndpoints()) {
root.add(new Link(request.getContextPath() + l.getHref(), l.getRel()));
}
return root;
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.app.rest.converter;
import java.sql.SQLException;
import java.util.List;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.CheckSumRest;
import org.dspace.content.Bundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This is the converter from/to the Bitstream in the DSpace API data model and the REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component
public class BitstreamConverter
extends DSpaceObjectConverter<org.dspace.content.Bitstream, org.dspace.app.rest.model.BitstreamRest> {
@Autowired(required = true)
BitstreamFormatConverter bfConverter;
@Override
public org.dspace.content.Bitstream toModel(org.dspace.app.rest.model.BitstreamRest obj) {
return super.toModel(obj);
}
@Override
public BitstreamRest fromModel(org.dspace.content.Bitstream obj) {
BitstreamRest b = super.fromModel(obj);
List<Bundle> bundles = null;
try {
bundles = obj.getBundles();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (bundles != null && bundles.size() > 0) {
b.setBundleName(bundles.get(0).getName());
}
CheckSumRest checksum = new CheckSumRest();
checksum.setCheckSumAlgorithm(obj.getChecksumAlgorithm());
checksum.setValue(obj.getChecksum());
b.setCheckSum(checksum);
BitstreamFormatRest format = null;
try {
format = bfConverter.fromModel(obj.getFormat(null));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b.setFormat(format);
b.setSizeBytes(obj.getSize());
return b;
}
@Override
protected BitstreamRest newInstance() {
return new BitstreamRest();
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.app.rest.converter;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.springframework.stereotype.Component;
/**
* This is the converter from/to the BitstreamFormat in the DSpace API data model and
* the REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@Component
public class BitstreamFormatConverter extends DSpaceConverter<org.dspace.content.BitstreamFormat, BitstreamFormatRest> {
@Override
public BitstreamFormatRest fromModel(org.dspace.content.BitstreamFormat obj) {
BitstreamFormatRest bf = new BitstreamFormatRest();
bf.setDescription(obj.getDescription());
bf.setExtensions(bf.getExtensions());
bf.setId(obj.getID());
bf.setMimetype(obj.getMIMEType());
bf.setShortDescription(obj.getShortDescription());
bf.setInternal(obj.isInternal());
return bf;
}
@Override
public org.dspace.content.BitstreamFormat toModel(BitstreamFormatRest obj) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,37 @@
/**
* 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.app.rest.converter;
import org.dspace.app.rest.model.CollectionRest;
import org.springframework.stereotype.Component;
/**
* This is the converter from/to the Collection in the DSpace API data model and
* the REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component
public class CollectionConverter
extends DSpaceObjectConverter<org.dspace.content.Collection, org.dspace.app.rest.model.CollectionRest> {
@Override
public org.dspace.content.Collection toModel(org.dspace.app.rest.model.CollectionRest obj) {
return (org.dspace.content.Collection) super.toModel(obj);
}
@Override
public CollectionRest fromModel(org.dspace.content.Collection obj) {
return (CollectionRest) super.fromModel(obj);
}
@Override
protected CollectionRest newInstance() {
return new CollectionRest();
}
}

View File

@@ -0,0 +1,37 @@
/**
* 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.app.rest.converter;
import org.dspace.app.rest.model.CommunityRest;
import org.springframework.stereotype.Component;
/**
* This is the converter from/to the community in the DSpace API data model and
* the REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component
public class CommunityConverter
extends DSpaceObjectConverter<org.dspace.content.Community, org.dspace.app.rest.model.CommunityRest> {
@Override
public org.dspace.content.Community toModel(org.dspace.app.rest.model.CommunityRest obj) {
return (org.dspace.content.Community) super.toModel(obj);
}
@Override
public CommunityRest fromModel(org.dspace.content.Community obj) {
return (CommunityRest) super.fromModel(obj);
}
@Override
protected CommunityRest newInstance() {
return new CommunityRest();
}
}

View File

@@ -0,0 +1,21 @@
/**
* 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.app.rest.converter;
import org.springframework.core.convert.converter.Converter;
public abstract class DSpaceConverter<M, R> implements Converter<M, R> {
@Override
public R convert(M source) {
return fromModel(source);
}
public abstract R fromModel(M obj);
public abstract M toModel(R obj);
}

View File

@@ -0,0 +1,58 @@
/**
* 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.app.rest.converter;
import java.util.ArrayList;
import java.util.List;
import org.dspace.app.rest.model.MetadataEntryRest;
import org.dspace.content.DSpaceObject;
import org.dspace.content.MetadataValue;
import org.springframework.core.convert.converter.Converter;
/**
*
* This is the base converter from/to objects in the DSpace API data model and
* the REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
* @param <M>
* the Class in the DSpace API data model
* @param <R>
* the Class in the DSpace REST data model
*/
public abstract class DSpaceObjectConverter<M extends DSpaceObject, R extends org.dspace.app.rest.model.DSpaceObjectRest>
extends DSpaceConverter<M, R> {
@Override
public R fromModel(M obj) {
R resource = newInstance();
resource.setHandle(obj.getHandle());
if (obj.getID() != null) {
resource.setUuid(obj.getID().toString());
}
resource.setName(obj.getName());
List<MetadataEntryRest> metadata = new ArrayList<MetadataEntryRest>();
for (MetadataValue mv : obj.getMetadata()) {
MetadataEntryRest me = new MetadataEntryRest();
me.setKey(mv.getMetadataField().toString('.'));
me.setValue(mv.getValue());
me.setLanguage(mv.getLanguage());
metadata.add(me);
}
resource.setMetadata(metadata);
return resource;
}
@Override
public M toModel(R obj) {
return null;
}
protected abstract R newInstance();
}

View File

@@ -0,0 +1,83 @@
/**
* 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.app.rest.converter;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This is the converter from/to the Item in the DSpace API data model and the
* REST data model
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component
public class ItemConverter extends DSpaceObjectConverter<org.dspace.content.Item, org.dspace.app.rest.model.ItemRest> {
@Autowired(required = true)
private CollectionConverter collectionConverter;
@Autowired(required = true)
private BitstreamConverter bitstreamConverter;
private static final Logger log = Logger.getLogger(ItemConverter.class);
@Override
public ItemRest fromModel(org.dspace.content.Item obj) {
ItemRest item = super.fromModel(obj);
item.setInArchive(obj.isArchived());
item.setDiscoverable(obj.isDiscoverable());
item.setWithdrawn(obj.isWithdrawn());
item.setLastModified(obj.getLastModified());
try {
Collection c = obj.getOwningCollection();
if (c != null) {
item.setOwningCollection(collectionConverter.fromModel(c));
}
} catch (Exception e) {
log.error("Error setting owning collection for item"+item.getHandle(), e);
}
try {
Collection c = obj.getTemplateItemOf();
if (c != null) {
item.setTemplateItemOf(collectionConverter.fromModel(c));
}
} catch (Exception e) {
log.error("Error setting template item of for item "+item.getHandle(), e);
}
List<BitstreamRest> bitstreams = new ArrayList<BitstreamRest>();
for (Bundle bun : obj.getBundles()) {
for (Bitstream bit : bun.getBitstreams()) {
BitstreamRest bitrest = bitstreamConverter.fromModel(bit);
bitstreams.add(bitrest);
}
}
item.setBitstreams(bitstreams);
return item;
}
@Override
public org.dspace.content.Item toModel(ItemRest obj) {
// TODO Auto-generated method stub
return null;
}
@Override
protected ItemRest newInstance() {
return new ItemRest();
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.app.rest.exception;
/**
* This is an exception to manage information about pagination errors.
* Out-of-order or other invalid requests
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class PaginationException extends RuntimeException {
long total;
public PaginationException(long total) {
this.total = total;
}
public long getTotal() {
return total;
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.app.rest.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* This is the exception to capture details about call to inexistent resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "This endpoint is not found in the system")
public class RepositoryNotFoundException extends RuntimeException {
String model;
public RepositoryNotFoundException(String model) {
this.model = model;
}
}

View File

@@ -0,0 +1,72 @@
/**
* 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.app.rest.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Servlet Filter whose only role is to clean up open Context objects in
* the request. (These Context objects may have been created by Controllers
* in order to populate Views).
*
* @author Tim Donohue
* @see ContextUtil
*/
public class DSpaceRequestContextFilter implements Filter
{
private static final Logger log = LoggerFactory.getLogger(DSpaceRequestContextFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
//noop
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
Context context = null;
try
{
// First, process any other servlet filters, along with the controller & view
chain.doFilter(request, response);
// *After* view was processed, check for an open Context object in the ServletRequest
// (This Context object may have been opened by a @Controller via ContextUtil.obtainContext())
context = (Context) request.getAttribute(ContextUtil.DSPACE_CONTEXT);
}
finally
{
// Abort the context if it's still valid, thus closing any open
// database connections
if ((context != null) && context.isValid())
{
ContextUtil.abortContext(request);
}
}
}
@Override
public void destroy()
{
//noop
}
}

View File

@@ -0,0 +1,36 @@
/**
* 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.app.rest.model;
import java.io.Serializable;
import org.springframework.hateoas.Identifiable;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Base class for any REST resource that need to be addressable
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
* @param <T>
* the class of the resource identifier
*/
public abstract class BaseObjectRest<T extends Serializable> implements Identifiable<T>, RestModel {
@JsonIgnore
protected T id;
@Override
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}

View File

@@ -0,0 +1,95 @@
/**
* 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.app.rest.model;
import java.util.List;
import org.dspace.app.rest.RestResourceController;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* The BitstreamFormat REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class BitstreamFormatRest extends BaseObjectRest<Integer> {
public static final String NAME = "bitstreamformat";
private String shortDescription;
private String description;
private String mimetype;
private int supportLevel;
private boolean internal;
private List<String> extensions;
public String getShortDescription() {
return shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getMimetype() {
return mimetype;
}
public void setMimetype(String mimetype) {
this.mimetype = mimetype;
}
public int getSupportLevel() {
return supportLevel;
}
public void setSupportLevel(int supportLevel) {
this.supportLevel = supportLevel;
}
public boolean isInternal() {
return internal;
}
public void setInternal(boolean internal) {
this.internal = internal;
}
public List<String> getExtensions() {
return extensions;
}
public void setExtensions(List<String> extensions) {
this.extensions = extensions;
}
@Override
public String getType() {
return NAME;
}
@Override
@JsonIgnore
public Class getController() {
return RestResourceController.class;
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.app.rest.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
/**
* The Bitstream REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public class BitstreamRest extends DSpaceObjectRest {
public static final String NAME = "bitstream";
private String bundleName;
// avoid to serialize this object inline as we want a full resource embedded
// TODO extends this annotation to provide information about lazy loading
// and projection behavior
@JsonProperty(access = Access.WRITE_ONLY)
private BitstreamFormatRest format;
private Long sizeBytes;
private CheckSumRest checkSum;
private Integer sequenceId;
public String getBundleName() {
return bundleName;
}
public void setBundleName(String bundleName) {
this.bundleName = bundleName;
}
public BitstreamFormatRest getFormat() {
return format;
}
public void setFormat(BitstreamFormatRest format) {
this.format = format;
}
public Long getSizeBytes() {
return sizeBytes;
}
public void setSizeBytes(Long sizeBytes) {
this.sizeBytes = sizeBytes;
}
public CheckSumRest getCheckSum() {
return checkSum;
}
public void setCheckSum(CheckSumRest checkSum) {
this.checkSum = checkSum;
}
public Integer getSequenceId() {
return sequenceId;
}
public void setSequenceId(Integer sequenceId) {
this.sequenceId = sequenceId;
}
@Override
public String getType() {
return NAME;
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.app.rest.model;
/**
* The CheckSum REST Resource. It is not addressable directly, only used as
* inline object in the Bitstream resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class CheckSumRest {
String checkSumAlgorithm;
String value;
public String getCheckSumAlgorithm() {
return checkSumAlgorithm;
}
public void setCheckSumAlgorithm(String checkSumAlgorithm) {
this.checkSumAlgorithm = checkSumAlgorithm;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.app.rest.model;
/**
* The Collection REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class CollectionRest extends DSpaceObjectRest {
public static final String NAME = "collection";
@Override
public String getType() {
return NAME;
}
}

View File

@@ -0,0 +1,24 @@
/**
* 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.app.rest.model;
/**
* The Community REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class CommunityRest extends DSpaceObjectRest {
public static final String NAME = "community";
@Override
public String getType() {
return NAME;
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.app.rest.model;
import java.util.List;
import org.dspace.app.rest.RestResourceController;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Base REST representation for all the DSpaceObjects
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public abstract class DSpaceObjectRest extends BaseObjectRest<String> {
@JsonIgnore
private String uuid;
private String name;
private String handle;
private String type;
List<MetadataEntryRest> metadata;
@Override
@JsonIgnore
public String getId() {
return uuid;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHandle() {
return handle;
}
public void setHandle(String handle) {
this.handle = handle;
}
public List<MetadataEntryRest> getMetadata() {
return metadata;
}
public void setMetadata(List<MetadataEntryRest> metadata) {
this.metadata = metadata;
}
@Override
@JsonIgnore
public Class getController() {
return RestResourceController.class;
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.app.rest.model;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* The Item REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class ItemRest extends DSpaceObjectRest {
public static final String NAME = "item";
private boolean inArchive = false;
private boolean discoverable = false;
private boolean withdrawn = false;
private Date lastModified = new Date();
@JsonIgnore
private CollectionRest owningCollection;
@JsonIgnore
private CollectionRest templateItemOf;
//private EPerson submitter;
List<BitstreamRest> bitstreams;
@Override
public String getType() {
return NAME;
}
public boolean getInArchive() {
return inArchive;
}
public void setInArchive(boolean inArchive) {
this.inArchive = inArchive;
}
public boolean getDiscoverable() {
return discoverable;
}
public void setDiscoverable(boolean discoverable) {
this.discoverable = discoverable;
}
public boolean getWithdrawn() {
return withdrawn;
}
public void setWithdrawn(boolean withdrawn) {
this.withdrawn = withdrawn;
}
public Date getLastModified() {
return lastModified;
}
public void setLastModified(Date lastModified){
this.lastModified = lastModified;
}
public CollectionRest getOwningCollection() {
return owningCollection;
}
public void setOwningCollection(CollectionRest owningCollection){
this.owningCollection = owningCollection;
}
public CollectionRest getTemplateItemOf() {
return templateItemOf;
}
public void setTemplateItemOf(CollectionRest templateItemOf){
this.templateItemOf = templateItemOf;
}
public List<BitstreamRest> getBitstreams() {
return bitstreams;
}
public void setBitstreams(List<BitstreamRest> bitstreams) {
this.bitstreams = bitstreams;
}
}

View File

@@ -0,0 +1,48 @@
/**
* 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.app.rest.model;
/**
* An embeddable representation of the Metadata to use in with DSpace REST
* Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class MetadataEntryRest {
String key;
String value;
String language;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
}

View File

@@ -0,0 +1,20 @@
/**
* 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.app.rest.model;
/**
* Methods to implement to make a REST resource addressable
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public interface RestModel {
public String getType();
public Class getController();
}

View File

@@ -0,0 +1,25 @@
/**
* 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.app.rest.model.hateoas;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.dspace.app.rest.utils.Utils;
/**
* BitstreamFormat Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RelNameDSpaceResource(BitstreamFormatRest.NAME)
public class BitstreamFormatResource extends DSpaceResource<BitstreamFormatRest> {
public BitstreamFormatResource(BitstreamFormatRest bf, Utils utils, String... rels) {
super(bf, utils, rels);
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.app.rest.model.hateoas;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.utils.Utils;
/**
* Bitstream Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RelNameDSpaceResource(BitstreamRest.NAME)
public class BitstreamResource extends DSpaceResource<BitstreamRest> {
public BitstreamResource(BitstreamRest bs, Utils utils, String... rels) {
super(bs, utils, rels);
// if (bs.getFormat() != null) {
// BitstreamFormatResource bfr = new BitstreamFormatResource(bs.getFormat());
// this.add(new Link(bfr.getLink(Link.REL_SELF).getHref(), "bitstreamformat"));
// }
}
}

View File

@@ -0,0 +1,25 @@
/**
* 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.app.rest.model.hateoas;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.utils.Utils;
/**
* Item Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RelNameDSpaceResource(CollectionRest.NAME)
public class CollectionResource extends DSpaceResource<CollectionRest> {
public CollectionResource(CollectionRest collection, Utils utils, String... rels) {
super(collection, utils, rels);
}
}

View File

@@ -0,0 +1,32 @@
/**
* 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/
*/
/**
* 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.app.rest.model.hateoas;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.utils.Utils;
/**
* Item Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RelNameDSpaceResource(CommunityRest.NAME)
public class CommunityResource extends DSpaceResource<CommunityRest> {
public CommunityResource(CommunityRest community, Utils utils, String... rels) {
super(community, utils, rels);
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.app.rest.model.hateoas;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
/**
* A DSpace Relation Provider that use the RelNameDSpaceResource to use the
* right names for the embedded collection when a DSpaceResource is requested
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class DSpaceRelProvider extends EvoInflectorRelProvider {
@Override
public String getItemResourceRelFor(Class<?> type) {
if (DSpaceResource.class.isAssignableFrom(type)) {
return type.getAnnotation(RelNameDSpaceResource.class).value();
}
return super.getItemResourceRelFor(type);
}
}

View File

@@ -0,0 +1,83 @@
/**
* 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.app.rest.model.hateoas;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.utils.Utils;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
/**
* A base class for DSpace Rest HAL Resource. The HAL Resource wraps the REST
* Resource adding support for the links and embedded resources. Each property
* of the wrapped REST resource is automatically translated in a link and the
* available information included as embedded resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public abstract class DSpaceResource<T extends RestModel> extends ResourceSupport {
@JsonUnwrapped
private final T data;
@JsonProperty(value = "_embedded")
@JsonInclude(Include.NON_EMPTY)
private Map<String, Object> embedded = new HashMap<String, Object>();
public DSpaceResource(T data, Utils utils, String... rels) {
this.data = data;
if (data != null) {
try {
for (PropertyDescriptor pd : Introspector.getBeanInfo(data.getClass()).getPropertyDescriptors()) {
Method readMethod = pd.getReadMethod();
if (readMethod != null && !"class".equals(pd.getName())) {
if (RestModel.class.isAssignableFrom(readMethod.getReturnType())) {
this.add(utils.linkToSubResource(data, pd.getName()));
RestModel linkedObject = (RestModel) readMethod.invoke(data);
if (linkedObject != null) {
embedded.put(pd.getName(),
utils.getResourceRepository(linkedObject.getType()).wrapResource(linkedObject));
} else {
embedded.put(pd.getName(), null);
}
Method writeMethod = pd.getWriteMethod();
writeMethod.invoke(data, new Object[] { null });
}
}
}
} catch (IntrospectionException | IllegalArgumentException | IllegalAccessException
| InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
this.add(utils.linkToSingleResource(data, Link.REL_SELF));
}
}
public Map<String, Object> getEmbedded() {
return embedded;
}
public T getData() {
return data;
}
}

View File

@@ -0,0 +1,25 @@
/**
* 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.app.rest.model.hateoas;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.utils.Utils;
/**
* Item Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RelNameDSpaceResource(ItemRest.NAME)
public class ItemResource extends DSpaceResource<ItemRest> {
public ItemResource(ItemRest item, Utils utils, String... rels) {
super(item, utils, rels);
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.app.rest.model.hateoas;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation allows to set the name to use for REST collections
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RelNameDSpaceResource {
String value();
}

View File

@@ -0,0 +1,76 @@
/**
* 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.app.rest.repository;
import java.sql.SQLException;
import java.util.List;
import org.dspace.app.rest.converter.BitstreamFormatConverter;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.dspace.app.rest.model.hateoas.BitstreamFormatResource;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage BitstreamFormat Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component(BitstreamFormatRest.NAME)
public class BitstreamFormatRestRepository extends DSpaceRestRepository<BitstreamFormatRest, Integer> {
BitstreamFormatService bfs = ContentServiceFactory.getInstance().getBitstreamFormatService();
@Autowired
BitstreamFormatConverter converter;
public BitstreamFormatRestRepository() {
System.out.println("Repository initialized by Spring");
}
@Override
public BitstreamFormatRest findOne(Context context, Integer id) {
BitstreamFormat bit = null;
try {
bit = bfs.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (bit == null) {
return null;
}
return converter.fromModel(bit);
}
@Override
public Page<BitstreamFormatRest> findAll(Context context, Pageable pageable) {
List<BitstreamFormat> bit = null;
try {
bit = bfs.findAll(context);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Page<BitstreamFormatRest> page = utils.getPage(bit, pageable).map(converter);
return page;
}
@Override
public Class<BitstreamFormatRest> getDomainClass() {
return BitstreamFormatRest.class;
}
@Override
public BitstreamFormatResource wrapResource(BitstreamFormatRest bs, String... rels) {
return new BitstreamFormatResource(bs, utils, rels);
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.app.rest.repository;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.dspace.app.rest.converter.BitstreamConverter;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.hateoas.BitstreamResource;
import org.dspace.content.Bitstream;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage Bitstream Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component(BitstreamRest.NAME)
public class BitstreamRestRepository extends DSpaceRestRepository<BitstreamRest, UUID> {
BitstreamService bs = ContentServiceFactory.getInstance().getBitstreamService();
@Autowired
BitstreamConverter converter;
public BitstreamRestRepository() {
System.out.println("Repository initialized by Spring");
}
@Override
public BitstreamRest findOne(Context context, UUID id) {
Bitstream bit = null;
try {
bit = bs.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (bit == null) {
return null;
}
return converter.fromModel(bit);
}
@Override
public Page<BitstreamRest> findAll(Context context, Pageable pageable) {
List<Bitstream> bit = new ArrayList<Bitstream>();
Iterator<Bitstream> it = null;
int total = 0;
try {
total = bs.countTotal(context);
it = bs.findAll(context, pageable.getPageSize(), pageable.getOffset());
while(it.hasNext()) {
bit.add(it.next());
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Page<BitstreamRest> page = new PageImpl<Bitstream>(bit, pageable, total).map(converter);
return page;
}
@Override
public Class<BitstreamRest> getDomainClass() {
return BitstreamRest.class;
}
@Override
public BitstreamResource wrapResource(BitstreamRest bs, String... rels) {
return new BitstreamResource(bs, utils, rels);
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.app.rest.repository;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.hateoas.CollectionResource;
import org.dspace.content.Collection;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage Item Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component(CollectionRest.NAME)
public class CollectionRestRepository extends DSpaceRestRepository<CollectionRest, UUID> {
CollectionService cs = ContentServiceFactory.getInstance().getCollectionService();
@Autowired
CollectionConverter converter;
public CollectionRestRepository() {
System.out.println("Repository initialized by Spring");
}
@Override
public CollectionRest findOne(Context context, UUID id) {
Collection collection = null;
try {
collection = cs.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (collection == null) {
return null;
}
return converter.fromModel(collection);
}
@Override
public Page<CollectionRest> findAll(Context context, Pageable pageable) {
List<Collection> it = null;
List<Collection> collections = new ArrayList<Collection>();
int total = 0;
try {
total = cs.countTotal(context);
it = cs.findAll(context, pageable.getPageSize(), pageable.getOffset());
for (Collection c: it) {
collections.add(c);
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Page<CollectionRest> page = new PageImpl<Collection>(collections, pageable, total).map(converter);
return page;
}
@Override
public Class<CollectionRest> getDomainClass() {
return CollectionRest.class;
}
@Override
public CollectionResource wrapResource(CollectionRest collection, String... rels) {
return new CollectionResource(collection, utils, rels);
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.app.rest.repository;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.dspace.app.rest.converter.CommunityConverter;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.hateoas.CommunityResource;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage Item Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component(CommunityRest.NAME)
public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest, UUID> {
CommunityService cs = ContentServiceFactory.getInstance().getCommunityService();
@Autowired
CommunityConverter converter;
public CommunityRestRepository() {
System.out.println("Repository initialized by Spring");
}
@Override
public CommunityRest findOne(Context context, UUID id) {
Community community = null;
try {
community = cs.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (community == null) {
return null;
}
return converter.fromModel(community);
}
@Override
public Page<CommunityRest> findAll(Context context, Pageable pageable) {
List<Community> it = null;
List<Community> communities = new ArrayList<Community>();
int total = 0;
try {
total = cs.countTotal(context);
it = cs.findAll(context, pageable.getPageSize(), pageable.getOffset());
for (Community c: it) {
communities.add(c);
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Page<CommunityRest> page = new PageImpl<Community>(communities, pageable, total).map(converter);
return page;
}
@Override
public Class<CommunityRest> getDomainClass() {
return CommunityRest.class;
}
@Override
public CommunityResource wrapResource(CommunityRest community, String... rels) {
return new CommunityResource(community, utils, rels);
}
}

View File

@@ -0,0 +1,134 @@
/**
* 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.app.rest.repository;
import java.io.Serializable;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.dspace.utils.DSpace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* This is the base class for any Rest Repository. It add a DSpaceContext to the
* normal Spring Data Repository methods signature and assure that the
* repository is able to wrap a DSpace Rest Object in a HAL Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public abstract class DSpaceRestRepository<T extends RestModel, ID extends Serializable>
implements PagingAndSortingRepository<T, ID> {
@Autowired
protected Utils utils;
protected RequestService requestService = new DSpace().getRequestService();
@Override
public <S extends T> S save(S entity) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends T> Iterable<S> save(Iterable<S> entities) {
// TODO Auto-generated method stub
return null;
}
@Override
public T findOne(ID id) {
Context context = obtainContext();
return findOne(context, id);
}
public abstract T findOne(Context context, ID id);
@Override
public boolean exists(ID id) {
// TODO Auto-generated method stub
return false;
}
@Override
public Iterable<T> findAll() {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
public Iterable<T> findAll(Iterable<ID> ids) {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
public long count() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void delete(ID id) {
// TODO Auto-generated method stub
}
@Override
public void delete(T entity) {
// TODO Auto-generated method stub
}
@Override
public void delete(Iterable<? extends T> entities) {
// TODO Auto-generated method stub
}
@Override
public void deleteAll() {
// TODO Auto-generated method stub
}
@Override
public Iterable<T> findAll(Sort sort) {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
public Page<T> findAll(Pageable pageable) {
Context context = obtainContext();
return findAll(context, pageable);
}
public abstract Page<T> findAll(Context context, Pageable pageable);
private Context obtainContext() {
Request currentRequest = requestService.getCurrentRequest();
Context context = (Context) currentRequest.getAttribute(ContextUtil.DSPACE_CONTEXT);
if (context != null && context.isValid()) {
return context;
}
context = new Context();
currentRequest.setAttribute(ContextUtil.DSPACE_CONTEXT, context);
return context;
}
public abstract Class<T> getDomainClass();
public abstract DSpaceResource<T> wrapResource(T model, String... rels);
}

View File

@@ -0,0 +1,90 @@
/**
* 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.app.rest.repository;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage Item Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component(ItemRest.NAME)
public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
ItemService is = ContentServiceFactory.getInstance().getItemService();
@Autowired
ItemConverter converter;
public ItemRestRepository() {
System.out.println("Repository initialized by Spring");
}
@Override
public ItemRest findOne(Context context, UUID id) {
Item item = null;
try {
item = is.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (item == null) {
return null;
}
return converter.fromModel(item);
}
@Override
public Page<ItemRest> findAll(Context context, Pageable pageable) {
Iterator<Item> it = null;
List<Item> items = new ArrayList<Item>();
int total = 0;
try {
total = is.countTotal(context);
it = is.findAll(context, pageable.getPageSize(), pageable.getOffset());
while (it.hasNext()) {
Item i = it.next();
items.add(i);
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Page<ItemRest> page = new PageImpl<Item>(items, pageable, total).map(converter);
return page;
}
@Override
public Class<ItemRest> getDomainClass() {
return ItemRest.class;
}
@Override
public ItemResource wrapResource(ItemRest item, String... rels) {
return new ItemResource(item, utils, rels);
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.app.rest.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
/**
* This class provide extra configuration for our Spring Boot Application
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Configuration
@EnableSpringDataWebSupport
@ComponentScan({ "org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils" })
public class ApplicationConfig {
@Value("${dspace.dir}")
private String dspaceHome;
public String getDspaceHome() {
return dspaceHome;
}
}

View File

@@ -0,0 +1,150 @@
/**
* 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.app.rest.utils;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
/**
* Miscellaneous UI utility methods methods for managing DSpace context.
*
* This class was "adapted" from the class of the same name in old XMLUI.
*
* @author Tim Donohue
*/
public class ContextUtil
{
/** The log4j logger */
private static final Logger log = Logger.getLogger(ContextUtil.class);
/** Where the context is stored on an HTTP Request object */
public static final String DSPACE_CONTEXT = "dspace.context";
/**
* Inspection method to check if a DSpace context has been created for this request.
*
* @param request
* the servlet request object
* @return True if a context has previously been created, false otherwise.
*/
public static boolean isContextAvailable(ServletRequest request) {
Object object = request.getAttribute(DSPACE_CONTEXT);
if (object instanceof Context)
return true;
else
return false;
}
/**
* Obtain a new context object. If a context object has already been created
* for this HTTP request, it is re-used, otherwise it is created.
*
* @param request
* the servlet request object
*
* @return a context object
*/
public static Context obtainContext(ServletRequest request) throws SQLException
{
Context context = (Context) request.getAttribute(DSPACE_CONTEXT);
if (context == null)
{
context = ContextUtil.intializeContext();
// Store the context in the request
request.setAttribute(DSPACE_CONTEXT, context);
}
return context;
}
/**
* Initialize a new Context object
* @return a DSpace Context Object
* @throws SQLException
*/
private static Context intializeContext() throws SQLException
{
// Create a new Context
Context context = new Context();
// Set the session ID
/**context.setExtraLogInfo("session_id="
+ request.getSession().getId());
AuthenticationUtil.resumeLogin(context, request);
// Set any special groups - invoke the authentication mgr.
int[] groupIDs = AuthenticationManager.getSpecialGroups(context, request);
for (int i = 0; i < groupIDs.length; i++)
{
context.setSpecialGroup(groupIDs[i]);
log.debug("Adding Special Group id="+String.valueOf(groupIDs[i]));
}
// Set the session ID and IP address
String ip = request.getRemoteAddr();
if (useProxies == null) {
useProxies = ConfigurationManager.getBooleanProperty("useProxies", false);
}
if(useProxies && request.getHeader("X-Forwarded-For") != null)
{
// This header is a comma delimited list
for(String xfip : request.getHeader("X-Forwarded-For").split(","))
{
if(!request.getHeader("X-Forwarded-For").contains(ip))
{
ip = xfip.trim();
}
}
}
context.setExtraLogInfo("session_id=" + request.getSession().getId() + ":ip_addr=" + ip);
*/
return context;
}
/**
* Check if a context exists for this request, if so complete the context.
*
* @param request
* The request object
*/
public static void completeContext(ServletRequest request) throws ServletException
{
Context context = (Context) request.getAttribute(DSPACE_CONTEXT);
if (context != null && context.isValid())
{
try
{
context.complete();
}
catch (SQLException e)
{
throw new ServletException(e);
}
}
}
public static void abortContext(ServletRequest request)
{
Context context = (Context) request.getAttribute(DSPACE_CONTEXT);
if (context != null && context.isValid())
{
context.abort();
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.app.rest.utils;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import java.util.List;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.exception.PaginationException;
import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
/**
* Collection of utility methods
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@Component
public class Utils {
@Autowired
ApplicationContext applicationContext;
public <T> Page<T> getPage(List<T> fullContents, Pageable pageable) {
int total = fullContents.size();
List<T> pageContent = null;
if (pageable.getOffset() > total) {
throw new PaginationException(total);
} else {
if (pageable.getOffset() + pageable.getPageSize() > total) {
pageContent = fullContents.subList(pageable.getOffset(), total);
} else {
pageContent = fullContents.subList(pageable.getOffset(), pageable.getOffset() + pageable.getPageSize());
}
return new PageImpl<T>(pageContent, pageable, total);
}
}
public Link linkToSingleResource(DSpaceResource r, String rel) {
RestModel data = r.getData();
return linkToSingleResource(data, rel);
}
public Link linkToSingleResource(RestModel data, String rel) {
return linkTo(data.getController(), data.getType()).slash(data).withRel(rel);
}
public Link linkToSubResource(RestModel data, String rel) {
return linkTo(data.getController(), data.getType()).slash(data).slash(rel).withRel(rel);
}
public DSpaceRestRepository getResourceRepository(String modelPlural) {
String model = makeSingular(modelPlural);
try {
return applicationContext.getBean(model, DSpaceRestRepository.class);
} catch (NoSuchBeanDefinitionException e) {
throw new RepositoryNotFoundException(model);
}
}
public String[] getRepositories() {
return applicationContext.getBeanNamesForType(DSpaceRestRepository.class);
}
public static String makeSingular(String modelPlural) {
//The old dspace res package includes the evo inflection library which has a plural() function but no singular function
if (modelPlural.equals("communities")) {
return CommunityRest.NAME;
}
return modelPlural.replaceAll("s$", "");
}
}

View File

@@ -0,0 +1,99 @@
#
# 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/
#
#
# Spring Boot application.properties
# Docs (including info on how to override these default settings)
# http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
# For common settings see:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
#
#
# TODO: Eventually would could think of "wiring" this up to use Commons Configuration as well
# See, for example: http://stackoverflow.com/questions/25271537/remote-propertysource
# and https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
########################
# DSpace Settings
#
# DSpace installation directory
dspace.dir=${dspace.dir}
#dspace.dir=d:/install/dspace7
########################
# Spring Boot Settings
#
# Testing an "application Name"
spring.application.name = DSpace Spring Rest
########################
# Spring DATA Rest settings
#
spring.data.rest.basePath=/api
########################
# Jackson serialization settings
#
spring.jackson.serialization.fail-on-empty-beans=false
########################
# Internationalization
#
# Base Path for our messages file (i18n)
spring.messages.basename=i18n/messages
spring.messages.encoding=UTF-8
###########################
# Embedded Tomcat Settings
#
# Change application port (for embedded Tomcat)
# Spring Boot app will be available at http://localhost:[server.port]/
server.port=8080
# Context path where application should be made available
# (Optional, defaults to root context)
#server.context-path=/spring-data-rest
# This creates a Tomcat context-param named "dspace.dir"
# and sets it to the value of the "dspace.dir" property (listed above)
server.context-parameters.dspace.dir=${dspace.dir}
# This creates a Tomcat context-param named "dspace-config"
# (Used by DSpaceContextListener to load the configurations)
# This is only needed in DSpace 5 or below to initialize ConfigurationManager
#server.context-parameters.dspace-config=${dspace.dir}/config/dspace.cfg
# Error handling settings
# Always include the fullstacktrace in error pages
# (Our Error page hides this stacktrace so it only is visible in HTML source)
# Can be set to "never" if you don't want it.
server.error.include-stacktrace = always
######################
# Spring Boot Autoconfigure
#
# DISABLE a few autoconfiguration scripts, as DSpace configures these already
# * DataSourceAutoConfiguration (DB connection / datasource)
# * FlywayAutoConfiguration (Flyway migrations)
#
# TODO: If we go with Spring boot, we should investigate whether it's worth
# re-enabling these and removing the custom DSpace initialization code
#spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \
# org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \
# org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
#########################
# Spring Boot Logging levels
#
#logging.level.org.springframework.boot=DEBUG
#logging.level.org.springframework.web=DEBUG
#logging.level.org.hibernate=ERROR

View File

@@ -0,0 +1,8 @@
#
# 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/
#

View File

@@ -0,0 +1,298 @@
<!--
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/
-->
<!doctype html>
<head>
<meta charset="utf-8">
<title>The HAL Browser (customized for Spring Data REST)</title>
<link rel="stylesheet" media="screen" href="api/browser/vendor/css/bootstrap.css" />
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
</style>
<link rel="stylesheet" media="screen" href="api/browser/vendor/css/bootstrap-responsive.css" />
<link rel="stylesheet" media="screen" href="api/browser/styles.css" />
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand">The HAL Browser</a>
<div class="nav-collapse">
<ul class="nav">
<li><a href="#/" id="entryPointLink">Go To Entry Point</a></li>
<li><a href="https://github.com/mikekelly/hal-browser">About The HAL Browser</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="browser" class="container-fluid"></div>
<script id="location-bar-template" type="text/template">
<form>
<div class="input-append span12 location-bar-container">
<input class="span11" id="appendedInputButton" type="text" value="<%= _.escape(url) %>">
<button class="btn" type="submit">Go!</button>
<span class="ajax-loader"></span>
</div>
</form>
</script>
<script id="links-template" type="text/template">
<h2>Links</h2>
<table class="table">
<thead>
<tr>
<th>rel</th>
<th>title</th>
<th>name / index</th>
<th>docs</th>
<th>GET</th>
<th>NON-GET</th>
</tr>
</thead>
<tbody>
<% _.each(links, function(obj, rel) { %>
<% if ($.isArray(obj)) { %>
<% _.each(obj, function(link, i) { %>
<tr>
<td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
<td><%= link.title || '' %></td>
<td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></a></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
<% } %>
</td>
<td>
<% if (link.templated === true) { %>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<% } else { %>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<% } %>
</td>
<td>
<a class="non-get btn btn-warning" href="<%= HAL.normalizeUrl(link.href) %>" title="Perform non-GET request">!</a>
</td>
</tr>
<% }); %>
<% } else { %>
<tr>
<td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
<td><%= obj.title || '' %></td>
<td><%= obj.name || '' %></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
<% } %>
</td>
<td>
<% if (obj.templated === true) { %>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<% } else { %>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<% } %>
</td>
<td>
<a class="non-get btn btn-warning" href="<%= HAL.normalizeUrl(obj.href) %>" title="Perform non-GET request">!</a>
</td>
</tr>
<% } %>
<% }) %>
</tbody>
</table>
</script>
<script id="properties-template" type="text/template">
<h2>Properties</h2>
<pre><%= _.escape(JSON.stringify(properties, null, HAL.jsonIndent)) %></pre>
</script>
<script id="request-headers-template" type="text/template">
<h2>Custom Request Headers</h2>
<textarea class="span12"></textarea>
</script>
<script id="response-headers-template" type="text/template">
<h2>Response Headers</h2>
<pre><%= status.code %> <%= status.text %>
<% _.each(headers, function(value, name) {
%><%= _.escape(name) %>: <%
if(HAL.isFollowableHeader(name)) {
%><a href="<%= HAL.normalizeUrl(value) %>" class="follow"><%
}
%><%= _.escape(value)
%><% if(HAL.isFollowableHeader(name)) {
%></a><%
} %>
<% }) %></pre>
</script>
<script id="response-body-template" type="text/template">
<h2>Response Body</h2>
<pre><%= _.escape(body) %></pre>
</script>
<script id="query-uri-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Expand URI Template</h3>
</div>
<form id="query" action="<%= href %>">
<div class="modal-body">
<p>URI Template:</p>
<pre><%- href %></pre>
<p>Input (JSON):</p>
<textarea><%= input %></textarea>
<p>Expanded URI:</p>
<pre class="preview">&nbsp;</pre>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Follow URI</button>
</div>
</form>
</script>
<script id="non-safe-request-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Make a non-GET request</h3>
</div>
<form class="non-safe" action="<%= href %>">
<div class="modal-body">
<p>Target URI</p>
<input name="url" type="text" class="url" value="<%= href %>" />
<p>Method:</p>
<input name="method" type="text" class="method" value="POST" />
<p>Headers:</p>
<textarea name="headers" class="headers" style="height: 100px">
Content-Type: application/json
<%= user_defined_headers %>
</textarea>
<p>Body:</p>
<textarea name="body" class="body" style="height: 200px">
{
}
</textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</script>
<script id="dynamic-request-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Create/Update</h3>
</div>
<form class="non-safe" action="<%= href %>">
<div class="modal-body" style="padding-top: 0px">
<div id="jsoneditor"></div>
<div class="well well-small" style="padding-bottom: 0px;">
<div class="container-fluid">
<div class="row-fluid">
<div class="control-group">
<label class="control-label" style="display: inline-block; font-weight: bold;">Action:</label>
<input name="method" type="text" class="method controls" style="width: 98%" value="POST" />
<input name="url" type="text" class="url controls" style="width: 98%" value="<%= href %>" />
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</script>
<script id="embedded-resources-template" type="text/template">
<h2>Embedded Resources</h2>
</script>
<script id="embedded-resource-template" type="text/template">
<div class="accordion-heading">
<a class="accordion-toggle" href="#"><%= resource.identifier %><% if (resource.title) { %>: <span class="embedded-resource-title"><%- resource.title %></span><% } %>
<% if (HAL.isUrl(resource.embed_rel)) { %>
<span class="dox pull-right" data-href="<%= HAL.normalizeUrl(HAL.buildUrl(resource.embed_rel)) %>">
<i class="icon-book"></i>
</span>
<% } %>
</a>
</div>
</script>
<script src="api/browser/vendor/js/jquery-1.10.2.min.js"></script>
<script src="api/browser/vendor/js/underscore.js"></script>
<script src="api/browser/vendor/js/backbone.js"></script>
<script src="api/browser/vendor/js/uritemplates.js"></script>
<script src="api/browser/vendor/js/URI.min.js"></script>
<script src="api/browser/vendor/js/bootstrap.js"></script>
<script src="api/browser/js/hal.js"></script>
<script src="api/browser/js/hal/browser.js"></script>
<script src="api/browser/js/hal/http/client.js"></script>
<script src="api/browser/js/hal/resource.js"></script>
<script src="api/browser/js/hal/views/browser.js"></script>
<script src="api/browser/js/hal/views/explorer.js"></script>
<script src="api/browser/js/hal/views/inspector.js"></script>
<script src="api/browser/js/hal/views/navigation.js"></script>
<script src="api/browser/js/hal/views/location_bar.js"></script>
<script src="api/browser/js/hal/views/request_headers.js"></script>
<script src="api/browser/js/hal/views/resource.js"></script>
<script src="api/browser/js/hal/views/properties.js"></script>
<script src="api/browser/js/hal/views/links.js"></script>
<script src="api/browser/js/hal/views/embedded_resources.js"></script>
<script src="api/browser/js/hal/views/embedded_resource.js"></script>
<script src="api/browser/js/hal/views/non_safe_request_dialog.js"></script>
<script src="api/browser/js/hal/views/query_uri_dialog.js"></script>
<script src="api/browser/js/hal/views/response.js"></script>
<script src="api/browser/js/hal/views/response_headers.js"></script>
<script src="api/browser/js/hal/views/response_body.js"></script>
<script src="api/browser/js/hal/views/documentation.js"></script>
<script src="api/browser/vendor/js/jsoneditor.js"></script>
<script src="api/browser/js/CustomPostForm.js"></script>
<script>
var browser = new HAL.Browser({
container: $('#browser'),
entryPoint: '/dspace-spring-rest/api'
});
Backbone.history.start();
</script>
</body>

View File

@@ -22,7 +22,9 @@
<!--Second level cache configuration-->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
</property>
<property name="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</property>

View File

@@ -105,5 +105,16 @@
<module>oai</module>
</modules>
</profile>
<profile>
<id>dspace-spring-rest</id>
<activation>
<file>
<exists>spring-rest/pom.xml</exists>
</file>
</activation>
<modules>
<module>spring-rest</module>
</modules>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,109 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace.modules</groupId>
<artifactId>spring-rest</artifactId>
<packaging>war</packaging>
<name>DSpace Spring Rest:: Local Customizations</name>
<description>Overlay REST customizations.
This is probably a temporary solution to the build problems. We like to investigate about
the possibility to remove the overlays enable a more flexible extension mechanism.
The use of web-fragment and spring mvc technology allow us to add request handlers
just adding new jar in the classloader</description>
<parent>
<artifactId>modules</artifactId>
<groupId>org.dspace</groupId>
<version>7.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<!-- This is the path to the root [dspace-src] directory. -->
<root.basedir>${basedir}/../../..</root.basedir>
</properties>
<build>
<plugins>
<!-- Unpack the "additions" module into our target directory,
so that any custom classes in that module can be included
into this WAR's WEB-INF/classes (see maven-war-plugin below). -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.dspace.modules</includeGroupIds>
<includeArtifactIds>additions</includeArtifactIds>
<!--NOTE: by default this will also unpack transitive dependencies. To disable, uncomment this next line:
<excludeTransitive>true</excludeTransitive>
-->
<outputDirectory>${project.build.directory}/additions</outputDirectory>
<excludes>META-INF/**</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archiveClasses>false</archiveClasses>
<!-- Filter the web.xml (needed for IDE compatibility/debugging) -->
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
<!-- Copy any 'additions' (see m-dependency-p above) into WEB-INF/classes.
This ensures they are loaded prior to dependencies in WEB-INF/lib
(per Servlet 3.0 spec, section 10.5), and allows them to override
default classes in this WAR -->
<webResources>
<resource>
<directory>${project.build.directory}/additions</directory>
<targetPath>WEB-INF/classes</targetPath>
</resource>
</webResources>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>oracle-support</id>
<activation>
<property>
<name>db.name</name>
<value>oracle</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.dspace.modules</groupId>
<artifactId>additions</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-spring-rest</artifactId>
<type>war</type>
</dependency>
</dependencies>
</project>

View File

@@ -668,6 +668,7 @@ Common usage:
<fileset dir="webapps">
<exclude name="**/web.xml" />
<exclude name="**/robots.txt" />
<exclude name="**/application.properties" />
</fileset>
</copy>
@@ -676,6 +677,7 @@ Common usage:
<fileset dir="webapps">
<include name="**/web.xml" />
<include name="**/robots.txt" />
<include name="**/application.properties" />
</fileset>
<filterchain>
<expandproperties />

48
pom.xml
View File

@@ -23,17 +23,13 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>${project.build.sourceEncoding}</project.reporting.outputEncoding>
<java.version>1.7</java.version>
<java.version>1.8</java.version>
<postgresql.driver.version>9.4.1211</postgresql.driver.version>
<solr.version>4.10.4</solr.version>
<jena.version>2.13.0</jena.version>
<slf4j.version>1.7.14</slf4j.version>
<!--
Hibernate version pinned to 4.2, 4.3 does not work with the spring version we are currently using
Upgrading the spring version will make the XMLUI crash
-->
<hibernate.version>4.2.21.Final</hibernate.version>
<spring.version>3.2.16.RELEASE</spring.version>
<slf4j.version>1.7.22</slf4j.version>
<hibernate.version>5.2.8.Final</hibernate.version>
<spring.version>4.3.6.RELEASE</spring.version>
<!-- 'root.basedir' is the path to the root [dspace-src] dir. It must be redefined by each child POM,
as it is used to reference the LICENSE_HEADER and *.properties file(s) in that directory. -->
<root.basedir>${basedir}</root.basedir>
@@ -64,7 +60,7 @@
</configuration>
</execution>
<!-- Make sure that we do not have conflicting dependencies-->
<execution>
<!-- <execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
@@ -74,7 +70,7 @@
<DependencyConvergence />
</rules>
</configuration>
</execution>
</execution> -->
</executions>
</plugin>
<!-- Used to compile all Java classes -->
@@ -672,6 +668,18 @@
</modules>
</profile>
<profile>
<id>dspace-spring-rest</id>
<activation>
<file>
<exists>dspace-spring-rest/pom.xml</exists>
</file>
</activation>
<modules>
<module>dspace-spring-rest</module>
</modules>
</profile>
<!--
The 'release' profile is used by the 'maven-release-plugin' (see above)
to actually perform a DSpace software release to Maven central.
@@ -694,6 +702,7 @@
<module>dspace-solr</module>
<module>dspace-sword</module>
<module>dspace-swordv2</module>
<module>dspace-spring-rest</module>
</modules>
<build>
<plugins>
@@ -843,6 +852,12 @@
<version>7.0-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-spring-rest</artifactId>
<version>7.0-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-solr</artifactId>
@@ -857,7 +872,6 @@
<type>war</type>
<classifier>skinny</classifier>
</dependency>
<!-- DSpace Localization Packages -->
<dependency>
<groupId>org.dspace</groupId>
@@ -1439,4 +1453,16 @@
</repository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<exclusions>
<exclusion>
<groupId>stax</groupId>
<artifactId>stax-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>