Merge branch 'master' into DS-4266-bitstream-format-crud

This commit is contained in:
Kevin Van de Velde
2019-06-24 15:58:09 +02:00
101 changed files with 2147 additions and 2927 deletions

View File

@@ -54,10 +54,7 @@ EXPOSE 8080 8009
ENV JAVA_OPTS=-Xmx2000m
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
RUN mv /usr/local/tomcat/webapps/ROOT /usr/local/tomcat/webapps/ROOT.bk && \
ln -s $DSPACE_INSTALL/webapps/spring-rest /usr/local/tomcat/webapps/ROOT && \
ln -s $DSPACE_INSTALL/webapps/spring-rest /usr/local/tomcat/webapps/spring-rest && \
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest && \
ln -s $DSPACE_INSTALL/webapps/oai /usr/local/tomcat/webapps/oai && \
ln -s $DSPACE_INSTALL/webapps/rdf /usr/local/tomcat/webapps/rdf && \
ln -s $DSPACE_INSTALL/webapps/sword /usr/local/tomcat/webapps/sword && \
ln -s $DSPACE_INSTALL/webapps/swordv2 /usr/local/tomcat/webapps/swordv2
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest

View File

@@ -54,16 +54,11 @@ EXPOSE 8080 8009
ENV JAVA_OPTS=-Xmx2000m
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
RUN mv /usr/local/tomcat/webapps/ROOT /usr/local/tomcat/webapps/ROOT.bk && \
ln -s $DSPACE_INSTALL/webapps/spring-rest /usr/local/tomcat/webapps/ROOT && \
ln -s $DSPACE_INSTALL/webapps/spring-rest /usr/local/tomcat/webapps/spring-rest && \
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest && \
ln -s $DSPACE_INSTALL/webapps/oai /usr/local/tomcat/webapps/oai && \
ln -s $DSPACE_INSTALL/webapps/rdf /usr/local/tomcat/webapps/rdf && \
ln -s $DSPACE_INSTALL/webapps/sword /usr/local/tomcat/webapps/sword && \
ln -s $DSPACE_INSTALL/webapps/swordv2 /usr/local/tomcat/webapps/swordv2
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest
COPY dspace/src/main/docker/test/solr_web.xml $DSPACE_INSTALL/webapps/solr/WEB-INF/web.xml
COPY dspace/src/main/docker/test/rest_web.xml $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/solr/WEB-INF/web.xml && \
sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml

View File

@@ -78,7 +78,7 @@ public class RequestItem implements ReloadableEntity<Integer> {
private Date request_date = null;
@Column(name = "accept_request")
private Boolean accept_request = null;
private boolean accept_request;
/**
* Protected constructor, create object using:
@@ -88,6 +88,7 @@ public class RequestItem implements ReloadableEntity<Integer> {
protected RequestItem() {
}
@Override
public Integer getID() {
return requestitem_id;
}

View File

@@ -8,10 +8,17 @@
package org.dspace.storage.rdbms;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.dspace.administer.MetadataImporter;
import org.dspace.administer.RegistryImportException;
import org.dspace.administer.RegistryLoader;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.NonUniqueMetadataException;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
@@ -21,6 +28,7 @@ import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.api.callback.FlywayCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* This is a FlywayCallback class which automatically updates the
@@ -49,7 +57,7 @@ public class DatabaseRegistryUpdater implements FlywayCallback {
private static final Logger log = LoggerFactory.getLogger(DatabaseRegistryUpdater.class);
/**
* Method to actually update our registries from latest configs
* Method to actually update our registries from latest configuration files.
*/
private void updateRegistries() {
ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService();
@@ -63,35 +71,32 @@ public class DatabaseRegistryUpdater implements FlywayCallback {
+ "registries" + File.separator;
// Load updates to Bitstream format registry (if any)
log.info("Updating Bitstream Format Registry based on " + base + "bitstream-formats.xml");
log.info("Updating Bitstream Format Registry based on {}bitstream-formats.xml", base);
RegistryLoader.loadBitstreamFormats(context, base + "bitstream-formats.xml");
// Load updates to Metadata schema registries (if any)
log.info("Updating Metadata Registries based on metadata type configs in " + base);
MetadataImporter.loadRegistry(base + "dublin-core-types.xml", true);
MetadataImporter.loadRegistry(base + "dcterms-types.xml", true);
MetadataImporter.loadRegistry(base + "local-types.xml", true);
MetadataImporter.loadRegistry(base + "relationship-formats.xml", true);
MetadataImporter.loadRegistry(base + "person-types.xml", true);
MetadataImporter.loadRegistry(base + "project-types.xml", true);
MetadataImporter.loadRegistry(base + "orgunit-types.xml", true);
MetadataImporter.loadRegistry(base + "journal-types.xml", true);
MetadataImporter.loadRegistry(base + "journalissue-types.xml", true);
MetadataImporter.loadRegistry(base + "journalvolume-types.xml", true);
MetadataImporter.loadRegistry(base + "eperson-types.xml", true);
MetadataImporter.loadRegistry(base + "sword-metadata.xml", true);
log.info("Updating Metadata Registries based on metadata type configs in {}", base);
for (String namespaceFile: config.getArrayProperty("registry.metadata.load")) {
log.info("Reading {}", namespaceFile);
MetadataImporter.loadRegistry(base + namespaceFile, true);
}
// Check if XML Workflow is enabled in workflow.cfg
if (WorkflowServiceFactory.getInstance().getWorkflowService() instanceof XmlWorkflowService) {
// If so, load in the workflow metadata types as well
MetadataImporter.loadRegistry(base + "workflow-types.xml", true);
String workflowTypes = "workflow-types.xml";
log.info("Reading {}", workflowTypes);
MetadataImporter.loadRegistry(base + workflowTypes, true);
}
context.restoreAuthSystemState();
// Commit changes and close context
context.complete();
log.info("All Bitstream Format Regitry and Metadata Registry updates were completed.");
} catch (Exception e) {
} catch (IOException | SQLException | ParserConfigurationException
| TransformerException | RegistryImportException
| AuthorizeException | NonUniqueMetadataException
| SAXException e) {
log.error("Error attempting to update Bitstream Format and/or Metadata Registries", e);
throw new RuntimeException("Error attempting to update Bitstream Format and/or Metadata Registries", e);
} finally {

View File

@@ -1,9 +1,9 @@
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>dspace-oai</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<name>DSpace OAI-PMH</name>
<description>DSpace OAI-PMH Web Application and API</description>
<description>DSpace OAI-PMH Extension</description>
<parent>
<artifactId>dspace-parent</artifactId>
@@ -16,34 +16,19 @@
<!-- This is the path to the root [dspace-src] directory. -->
<root.basedir>${basedir}/..</root.basedir>
<xoai.version>3.2.10</xoai.version>
<jtwig.version>2.0.1</jtwig.version>
<!-- NOTE: Once updated to Spring Boot v2, this should update to 5.87.0.RELEASE -->
<jtwig.version>5.86.1.RELEASE</jtwig.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
<!-- In version 2.1-alpha-1, this was incorrectly named warSourceExcludes -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<warSourceExcludes>WEB-INF/lib/*.jar</warSourceExcludes>
<!-- Filter the web.xml (needed for IDE compatibility/debugging) -->
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/main/webapp/**</exclude>
<!-- Ignore license header requirements on CSS/images/HTML/XSL -->
<exclude>src/main/resources/**</exclude>
<exclude>**/*.xsl</exclude>
</excludes>
</configuration>
@@ -106,74 +91,63 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<!-- Later version provided by SolrJ -->
<exclusion>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>wstx-asl</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Java Injection and MVC -->
<!-- Java Injection -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Needed to support Spring @Configuration classes (to register servlets/beans with Spring Boot webapp) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Templating Engine -->
<dependency>
<groupId>com.lyncode</groupId>
<artifactId>jtwig-spring</artifactId>
<groupId>org.jtwig</groupId>
<artifactId>jtwig-spring-boot-starter</artifactId>
<version>${jtwig.version}</version>
<exclusions>
<!-- We don't need spring-boot-starter-web, it is provided by our Spring Boot webapp -->
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<!-- More recent version is pulled in via solr-core -->
<exclusion>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
<!-- This exclusion may be removable once we update to Spring Boot v2 -->
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -283,7 +257,7 @@
<dependency>
<groupId>org.parboiled</groupId>
<artifactId>parboiled-core</artifactId>
<version>1.1.6</version>
<version>1.1.7</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -293,24 +267,4 @@
<scope>test</scope>
</dependency>
</dependencies>
<developers>
<developer>
<name>DSpace @ Lyncode</name>
<email>dspace@lyncode.com</email>
<organization>Lyncode</organization>
<organizationUrl>http://www.lyncode.com</organizationUrl>
</developer>
<developer>
<id>helix84</id>
<name>Ivan Másar</name>
<email>helix84@centrum.sk</email>
</developer>
<developer>
<name>Ariel J. Lira</name>
<email>arieljlira@gmail.com</email>
<organization>SeDiCI</organization>
<organizationUrl>http://sedici.unlp.edu.ar</organizationUrl>
</developer>
</developers>
</project>

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.configuration;
import static java.lang.Integer.MAX_VALUE;
import org.dspace.xoai.app.BasicConfiguration;
import org.dspace.xoai.services.api.xoai.ItemRepositoryResolver;
import org.dspace.xoai.services.impl.xoai.DSpaceItemRepositoryResolver;
import org.jtwig.spring.JtwigViewResolver;
import org.jtwig.spring.boot.config.JtwigViewResolverConfigurer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* OAI-PMH webapp configuration. Replaces the old web.xml
* <p>
* This @Configuration class is automatically discovered by Spring Boot via a @ComponentScan
* on the org.dspace.app.configuration package.
* <p>
*
*
* @author Tim Donohue
*/
@Configuration
// Import additional configuration and beans from BasicConfiguration
@Import(BasicConfiguration.class)
// Scan for controllers in this package
@ComponentScan("org.dspace.xoai.controller")
public class OAIWebConfig extends WebMvcConfigurerAdapter implements JtwigViewResolverConfigurer {
// Path where OAI is deployed. Defaults to "oai"
// NOTE: deployment on this path is handled by org.dspace.xoai.controller.DSpaceOAIDataProvider
@Value("${oai.path:oai}")
private String oaiPath;
private static final String TWIG_HTML_EXTENSION = ".twig.html";
private static final String VIEWS_LOCATION = "classpath:/templates/";
/**
* Ensure all resources under src/main/resources/static/ directory are available
* off the /{oai.path}/static subpath
**/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/" + oaiPath + "/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(MAX_VALUE);
}
/**
* Configure the Jtwig template engine for Spring Boot
* Ensures Jtwig looks for templates in proper location with proper extension
**/
@Override
public void configure(JtwigViewResolver viewResolver) {
viewResolver.setPrefix(VIEWS_LOCATION);
viewResolver.setSuffix(TWIG_HTML_EXTENSION);
}
@Bean
public ItemRepositoryResolver xoaiItemRepositoryResolver() {
return new DSpaceItemRepositoryResolver();
}
}

View File

@@ -1,62 +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/
*/
package org.dspace.xoai.app;
import static java.lang.Integer.MAX_VALUE;
import com.lyncode.jtwig.mvc.JtwigViewResolver;
import org.dspace.xoai.services.api.xoai.ItemRepositoryResolver;
import org.dspace.xoai.services.impl.xoai.DSpaceItemRepositoryResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Import( {
BasicConfiguration.class
})
@Configuration
@EnableWebMvc
@ComponentScan("org.dspace.xoai.controller")
public class DSpaceWebappConfiguration extends WebMvcConfigurerAdapter {
private static final String TWIG_HTML_EXTENSION = ".twig.html";
private static final String VIEWS_LOCATION = "/WEB-INF/views/";
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/")
.setCachePeriod(MAX_VALUE);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public ViewResolver viewResolver() {
JtwigViewResolver viewResolver = new JtwigViewResolver();
viewResolver.setPrefix(VIEWS_LOCATION);
viewResolver.setSuffix(TWIG_HTML_EXTENSION);
viewResolver.setCached(false);
return viewResolver;
}
@Bean
public ItemRepositoryResolver xoaiItemRepositoryResolver() {
return new DSpaceItemRepositoryResolver();
}
}

View File

@@ -40,6 +40,7 @@ import org.dspace.xoai.services.api.xoai.ItemRepositoryResolver;
import org.dspace.xoai.services.api.xoai.SetRepositoryResolver;
import org.dspace.xoai.services.impl.xoai.DSpaceResumptionTokenFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
@@ -49,6 +50,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
* @author Lyncode Development Team (dspace at lyncode dot com)
*/
@Controller
// Use the configured "oai.path" for all requests, or "/oai" by default
@RequestMapping("/${oai.path:oai}")
// Only enable this controller if "oai.enabled=true"
@ConditionalOnProperty("oai.enabled")
public class DSpaceOAIDataProvider {
private static final Logger log = getLogger(DSpaceOAIDataProvider.class);
@@ -67,7 +72,7 @@ public class DSpaceOAIDataProvider {
private DSpaceResumptionTokenFormatter resumptionTokenFormat = new DSpaceResumptionTokenFormatter();
@RequestMapping("/")
@RequestMapping({"", "/"})
public String indexAction(HttpServletResponse response, Model model) throws ServletException {
try {
XOAIManager manager = xoaiManagerResolver.getManager();

View File

@@ -10,6 +10,8 @@ package org.dspace.xoai.services.impl;
import java.sql.SQLException;
import java.util.Date;
import javax.persistence.NoResultException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.MetadataValue;
@@ -30,11 +32,16 @@ public class DSpaceEarliestDateResolver implements EarliestDateResolver {
@Override
public Date getEarliestDate(Context context) throws InvalidMetadataFieldException, SQLException {
String query = "SELECT MIN(text_value) as value FROM metadatavalue WHERE metadata_field_id = ?";
MetadataValueService metadataValueService = ContentServiceFactory.getInstance().getMetadataValueService();
MetadataValue minimum = metadataValueService.getMinimum(context,
fieldResolver.getFieldID(context, "dc.date.available"));
MetadataValue minimum = null;
try {
minimum = metadataValueService.getMinimum(context,
fieldResolver.getFieldID(context, "dc.date.available"));
} catch (NoResultException e) {
// This error only occurs if no metadataFields of this type exist (i.e. no minimum exists)
// It can be safely ignored in this scenario, as it implies the DSpace is empty.
}
if (null != minimum) {
String str = minimum.getValue();
try {

View File

@@ -27,6 +27,8 @@ import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.config.ConfigurationService;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponentsBuilder;
/**
* @author Lyncode Development Team (dspace at lyncode dot com)
@@ -68,17 +70,27 @@ public class DSpaceRepositoryConfiguration implements RepositoryConfiguration {
public String getBaseUrl() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
// Parse the current OAI "context" path out of the last HTTP request.
// (e.g. for "http://mydspace.edu/oai/request", the oaiContextPath is "request")
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromRequest(request);
List<String> pathSegments = builder.buildAndExpand().getPathSegments();
String oaiContextPath = pathSegments.get(pathSegments.size() - 1);
if (baseUrl == null) {
baseUrl = configurationService.getProperty("oai.url");
if (baseUrl == null) {
log.warn(
"{ OAI 2.0 :: DSpace } Not able to retrieve the oai.url property from oai.cfg. Falling back to " +
"request address");
// initialize baseUrl to a fallback "oai.url" which is the current request with OAI context removed.
baseUrl = request.getRequestURL().toString()
.replace(request.getPathInfo(), "");
.replace(oaiContextPath, "");
}
}
return baseUrl + request.getPathInfo();
// BaseURL is the path of OAI with the current OAI context appended
return baseUrl + "/" + oaiContextPath;
}
@Override

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -6,6 +6,12 @@
http://www.dspace.org/license/
#}
{#
DSpace OAI default index template. To override this template, place a customized version in
the [webapp]/WEB-INF/classes/templates/ folder, and reboot your servlet engine.
#}
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
@@ -13,8 +19,8 @@
<title>DSpace OAI-PMH Data Provider</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="{{ path 'static/js/jquery.js' }}" type="text/javascript"></script>
<script src="{{ path 'static/js/bootstrap.min.js' }}" type="text/javascript"></script>
<script src="static/js/jquery.js" type="text/javascript"></script>
<script src="static/js/bootstrap.min.js" type="text/javascript"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@@ -24,9 +30,9 @@
<![endif]-->
<link rel="stylesheet" href="{{ path 'static/css/bootstrap.min.css' }}" type="text/css" />
<link rel="stylesheet" href="{{ path 'static/css/bootstrap-theme.min.css' }}" type="text/css" />
<link rel="stylesheet" href="{{ path 'static/css/style.css' }}" type="text/css" />
<link rel="stylesheet" href="static/css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="static/css/bootstrap-theme.min.css" type="text/css" />
<link rel="stylesheet" href="static/css/style.css" type="text/css" />
</head>
<body>
@@ -56,11 +62,11 @@
<p class="vertical-space"></p>
<p class="list-group-item-text">
<div class="btn-group btn-group-justified">
<a class="btn btn-default" href="{{ concat('/', item.baseUrl) | path }}?verb=Identify">Identify</a>
<a class="btn btn-default" href="{{ concat('/', item.baseUrl) | path }}?verb=ListSets">List Sets</a>
<a class="btn btn-default" href="{{ concat('/', item.baseUrl) | path }}?verb=ListMetadataFormats">List Metadata Formats</a>
<a class="btn btn-default" href="{{ concat('/', item.baseUrl) | path }}?verb=ListIdentifiers&metadataPrefix=oai_dc">List Identifiers</a>
<a class="btn btn-default" href="{{ concat('/', item.baseUrl) | path }}?verb=ListRecords&metadataPrefix=oai_dc">List Records</a>
<a class="btn btn-default" href="{{ item.baseUrl }}?verb=Identify">Identify</a>
<a class="btn btn-default" href="{{ item.baseUrl }}?verb=ListSets">List Sets</a>
<a class="btn btn-default" href="{{ item.baseUrl }}?verb=ListMetadataFormats">List Metadata Formats</a>
<a class="btn btn-default" href="{{ item.baseUrl }}?verb=ListIdentifiers&metadataPrefix=oai_dc">List Identifiers</a>
<a class="btn btn-default" href="{{ item.baseUrl }}?verb=ListRecords&metadataPrefix=oai_dc">List Records</a>
</div>
</p>
</div>
@@ -73,11 +79,11 @@
<p><small>Design by Lyncode</small></p>
<p>
<a href="http://www.lyncode.com">
<img style="height: 20px;" src="{{ path '/static/img/lyncode.png' }}" alt="Lyncode" />
<img style="height: 20px;" src="static/img/lyncode.png" alt="Lyncode" />
</a>
</p>
</div>
</div> <!-- /container -->
</body>
</html>
</html>

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-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/
-->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- Acquires the DSpace Utility Class with initialized Service Manager -->
<bean id="dspace" class="org.dspace.utils.DSpace"/>
<!-- Acquires reference to EventService -->
<bean id="dspace.eventService" factory-bean="dspace" factory-method="getEventService"/>
<!-- Inject the Default LoggerUsageEventListener into the EventService -->
<bean class="org.dspace.usage.LoggerUsageEventListener">
<property name="eventService">
<ref bean="dspace.eventService"/>
</property>
</bean>
<!-- Inject the SolrLoggerUsageEventListener into the EventService -->
<bean class="org.dspace.statistics.SolrLoggerUsageEventListener">
<property name="eventService">
<ref bean="dspace.eventService"/>
</property>
</bean>
<!-- Google Analytics recording -->
<bean class="org.dspace.google.GoogleRecorderEventListener">
<property name="eventService">
<ref bean="dspace.eventService"/>
</property>
</bean>
<!-- TabFileUsageEventListener -->
<!-- Uncomment to enable
<bean class="org.dspace.usage.TabFileUsageEventListener">
<property name="eventService" >
<ref bean="dspace.eventService"/>
</property>
</bean>
-->
<!--
Uncomment to enable PassiveUsageEventListener
<bean class="org.dspace.app.statistics.PassiveUsageEventListener">
<property name="eventService" >
<ref bean="dspace.eventService"/>
</property>
</bean>
-->
</beans>

View File

@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-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/
-->
<web-app id="DSpace-OAI" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>XOAI Data Provider</display-name>
<context-param>
<description>The location of the DSpace home directory</description>
<param-name>dspace.dir</param-name>
<param-value>${dspace.dir}</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>${dspace.dir}/config/log4j2.xml</param-value>
<description>The location of the Log4J configuration</description>
</context-param>
<!-- Location of root application context configs (to be loaded by ContextLoaderListener below) -->
<!-- This contains the beans that initialize DSpace Services -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/*.xml</param-value>
</context-param>
<!-- Initializes the DSpace Context object -->
<listener>
<listener-class>org.dspace.app.util.DSpaceContextListener</listener-class>
</listener>
<!-- Initializes the DSpace Kernel -->
<listener>
<listener-class>
org.dspace.servicemanager.servlet.DSpaceKernelServletContextListener
</listener-class>
</listener>
<!-- Registers this DSpace webapp as "running" -->
<listener>
<listener-class>org.dspace.app.util.DSpaceWebappListener</listener-class>
</listener>
<!-- Load the root application context / beans (defined in contextConfigLocation above) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>oai</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- @Configuration class which defines our servlet context config -->
<!-- This defines beans that are specific to OAI webapp -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>org.dspace.xoai.app.DSpaceWebappConfiguration</param-value>
</init-param>
</servlet>
<!-- Load the oai servlet for all paths -->
<servlet-mapping>
<servlet-name>oai</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -10,11 +10,9 @@ package org.dspace.xoai.tests;
import static org.mockito.Mockito.mock;
import org.dspace.xoai.services.api.FieldResolver;
import org.dspace.xoai.services.api.config.ConfigurationService;
import org.dspace.xoai.services.api.context.ContextService;
import org.dspace.xoai.services.api.xoai.DSpaceFilterResolver;
import org.dspace.xoai.services.impl.xoai.BaseDSpaceFilterResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedConfigurationService;
import org.dspace.xoai.tests.helpers.stubs.StubbedFieldResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -29,14 +27,6 @@ public class DSpaceBasicTestConfiguration {
return new BaseDSpaceFilterResolver();
}
private StubbedConfigurationService configurationService = new StubbedConfigurationService();
@Bean
public ConfigurationService configurationService() {
return configurationService;
}
@Bean
public ContextService contextService() {
return mock(ContextService.class);

View File

@@ -1,123 +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/
*/
package org.dspace.xoai.tests;
import com.lyncode.xoai.dataprovider.services.api.ItemRepository;
import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.services.api.SetRepository;
import org.dspace.core.Context;
import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.cache.XOAICacheService;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.services.api.context.ContextService;
import org.dspace.xoai.services.api.context.ContextServiceException;
import org.dspace.xoai.services.api.xoai.IdentifyResolver;
import org.dspace.xoai.services.api.xoai.ItemRepositoryResolver;
import org.dspace.xoai.services.api.xoai.SetRepositoryResolver;
import org.dspace.xoai.services.impl.cache.DSpaceEmptyCacheService;
import org.dspace.xoai.services.impl.xoai.DSpaceIdentifyResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedEarliestDateResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedResourceResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedSetRepository;
import org.dspace.xoai.tests.helpers.stubs.StubbedXOAIManagerResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Import(DSpaceBasicTestConfiguration.class)
@Configuration
@EnableWebMvc
public class DSpaceTestConfiguration extends WebMvcConfigurerAdapter {
private static final String TWIG_HTML_EXTENSION = ".twig.html";
private static final String VIEWS_LOCATION = "/WEB-INF/views/";
@Bean
public ContextService contextService() {
return new ContextService() {
@Override
public Context getContext() throws ContextServiceException {
return null;
}
};
}
private StubbedResourceResolver resourceResolver = new StubbedResourceResolver();
@Bean
public ResourceResolver resourceResolver() {
return resourceResolver;
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix(VIEWS_LOCATION);
viewResolver.setSuffix(TWIG_HTML_EXTENSION);
// viewResolver.setCached(true);
// viewResolver.setTheme(null);
return viewResolver;
}
@Bean
public XOAIManagerResolver xoaiManagerResolver() {
return new StubbedXOAIManagerResolver();
}
@Bean
public XOAICacheService xoaiCacheService() {
return new DSpaceEmptyCacheService();
}
private StubbedSetRepository setRepository = new StubbedSetRepository();
@Bean
StubbedSetRepository setRepository() {
return setRepository;
}
@Bean
public ItemRepositoryResolver itemRepositoryResolver() {
return new ItemRepositoryResolver() {
@Override
public ItemRepository getItemRepository() throws ContextServiceException {
try {
return null;
} catch (Exception e) {
throw new ContextServiceException(e);
}
}
};
}
@Bean
public SetRepositoryResolver setRepositoryResolver() {
return new SetRepositoryResolver() {
@Override
public SetRepository getSetRepository() throws ContextServiceException {
return setRepository;
}
};
}
@Bean
public IdentifyResolver identifyResolver() {
return new DSpaceIdentifyResolver();
}
@Bean
public EarliestDateResolver earliestDateResolver() {
return new StubbedEarliestDateResolver();
}
}

View File

@@ -1,37 +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/
*/
package org.dspace.xoai.tests.helpers;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.mock.web.MockHttpServletRequest;
public class HttpRequestBuilder {
private MockHttpServletRequest request = new MockHttpServletRequest();
public HttpRequestBuilder withUrl(String url) {
try {
URI uri = new URI(url);
} catch (URISyntaxException e) {
// ASD
}
return this;
}
public HttpRequestBuilder usingGetMethod() {
request.setMethod("GET");
return this;
}
public HttpServletRequest build() {
return request;
}
}

View File

@@ -1,28 +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/
*/
package org.dspace.xoai.tests.helpers;
public class SyntacticSugar {
/**
* Default constructor
*/
private SyntacticSugar() { }
public static <T> T given(T elem) {
return elem;
}
public static <T> T the(T elem) {
return elem;
}
public static <T> T and(T elem) {
return elem;
}
}

View File

@@ -1,183 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import static com.lyncode.xoai.dataprovider.core.Granularity.Second;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.stream.XMLStreamException;
import com.lyncode.xoai.builders.dataprovider.ElementBuilder;
import com.lyncode.xoai.builders.dataprovider.MetadataBuilder;
import com.lyncode.xoai.dataprovider.exceptions.MetadataBindException;
import com.lyncode.xoai.dataprovider.exceptions.WritingXmlException;
import com.lyncode.xoai.dataprovider.xml.XmlOutputContext;
import com.lyncode.xoai.dataprovider.xml.xoai.Metadata;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.common.SolrInputDocument;
public class ItemRepositoryBuilder {
private final SolrClient solrServer;
public ItemRepositoryBuilder(SolrClient solrServer) {
this.solrServer = solrServer;
}
public ItemRepositoryBuilder withItem(DSpaceItemBuilder builder) {
try {
solrServer.add(index(builder));
solrServer.commit();
} catch (MetadataBindException | WritingXmlException | IOException
| SQLException | ParseException | XMLStreamException
| SolrServerException e) {
throw new RuntimeException(e);
}
return this;
}
private SolrInputDocument index(DSpaceItemBuilder item)
throws SQLException, MetadataBindException, ParseException, XMLStreamException, WritingXmlException {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("item.id", item.getId());
doc.addField("item.public", item.isPublic());
doc.addField("item.lastmodified", item.getLastModifiedDate());
doc.addField("item.submitter", item.getSubmitter());
doc.addField("item.handle", item.getHandle());
doc.addField("item.deleted", item.isDeleted());
for (String col : item.getCollections()) {
doc.addField("item.collections", col);
}
for (String col : item.getCommunities()) {
doc.addField("item.communities", col);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
XmlOutputContext context = XmlOutputContext.emptyContext(out, Second);
item.getMetadata().write(context);
context.getWriter().flush();
context.getWriter().close();
doc.addField("item.compile", out.toString());
return doc;
}
public static class DSpaceItemBuilder {
private final List<String> collections = new ArrayList<>();
private final List<String> communities = new ArrayList<>();
private final MetadataBuilder metadataBuilder = new MetadataBuilder();
private String handle;
private int id;
private String submitter;
private Date lastModifiedDate;
private boolean deleted;
private boolean aPublic = true;
public DSpaceItemBuilder withLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
return this;
}
public DSpaceItemBuilder withCollection(String colName) {
collections.add(colName);
return this;
}
public DSpaceItemBuilder withCommunity(String comName) {
communities.add(comName);
return this;
}
public DSpaceItemBuilder whichSsPublic() {
aPublic = true;
return this;
}
public DSpaceItemBuilder whichSsPrivate() {
aPublic = false;
return this;
}
public DSpaceItemBuilder whichIsDeleted() {
this.deleted = true;
return this;
}
public DSpaceItemBuilder whichIsNotDeleted() {
this.deleted = false;
return this;
}
public DSpaceItemBuilder withMetadata(String schema, String element, String value) {
metadataBuilder.withElement(new ElementBuilder().withName(schema).withField(element, value).build());
return this;
}
public String getHandle() {
return handle;
}
public DSpaceItemBuilder withHandle(String handle) {
this.handle = handle;
return this;
}
public DSpaceItemBuilder withSubmitter(String submitter) {
this.submitter = submitter;
return this;
}
public DSpaceItemBuilder withId(int id) {
this.id = id;
return this;
}
public int getId() {
return id;
}
public String getSubmitter() {
return submitter;
}
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public List<String> getCollections() {
return collections;
}
public List<String> getCommunities() {
return communities;
}
public Metadata getMetadata() {
return metadataBuilder.build();
}
public boolean isDeleted() {
return deleted;
}
public boolean isPublic() {
return aPublic;
}
}
}

View File

@@ -1,63 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import java.util.HashMap;
import java.util.Map;
import org.dspace.xoai.services.api.config.ConfigurationService;
public class StubbedConfigurationService implements ConfigurationService {
private Map<String, Object> values = new HashMap<String, Object>();
public StubbedConfigurationService hasBooleanProperty(String key, boolean value) {
values.put(key, value);
return this;
}
public StubbedConfigurationService hasBooleanProperty(String module, String key, boolean value) {
values.put(module + "." + key, value);
return this;
}
public StubbedConfigurationService hasProperty(String key, String value) {
values.put(key, value);
return this;
}
public StubbedConfigurationService withoutProperty(String key) {
values.remove(key);
return this;
}
public StubbedConfigurationService hasProperty(String module, String key, String value) {
values.put(module + "." + key, value);
return this;
}
@Override
public String getProperty(String key) {
return (String) values.get(key);
}
@Override
public String getProperty(String module, String key) {
return (String) values.get(module + "." + key);
}
@Override
public boolean getBooleanProperty(String module, String key, boolean defaultValue) {
Boolean value = (Boolean) values.get(module + "." + key);
if (value == null) {
return defaultValue;
} else {
return value;
}
}
}

View File

@@ -1,29 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import java.sql.SQLException;
import java.util.Date;
import org.dspace.core.Context;
import org.dspace.xoai.exceptions.InvalidMetadataFieldException;
import org.dspace.xoai.services.api.EarliestDateResolver;
public class StubbedEarliestDateResolver implements EarliestDateResolver {
private Date date = new Date();
public StubbedEarliestDateResolver is(Date date) {
this.date = date;
return this;
}
@Override
public Date getEarliestDate(Context context) throws InvalidMetadataFieldException, SQLException {
return date;
}
}

View File

@@ -1,44 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
public class StubbedResourceResolver implements ResourceResolver {
private static TransformerFactory factory = TransformerFactory.newInstance();
private Map<String, InputStream> inputStreamMap = new HashMap<String, InputStream>();
private Map<String, Transformer> transformerMap = new HashMap<String, Transformer>();
@Override
public InputStream getResource(String path) throws IOException {
return inputStreamMap.get(path);
}
@Override
public Transformer getTransformer(String path) throws IOException, TransformerConfigurationException {
return transformerMap.get(path);
}
public StubbedResourceResolver hasIdentityTransformerFor(String path) {
try {
transformerMap.put(path, factory.newTransformer());
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
}
return this;
}
}

View File

@@ -1,74 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import static java.lang.Math.min;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.util.ArrayList;
import java.util.List;
import com.lyncode.xoai.dataprovider.core.ListSetsResult;
import com.lyncode.xoai.dataprovider.core.Set;
import com.lyncode.xoai.dataprovider.services.api.SetRepository;
public class StubbedSetRepository implements SetRepository {
private List<Set> sets = new ArrayList<Set>();
private boolean supports = false;
@Override
public boolean supportSets() {
return supports;
}
@Override
public ListSetsResult retrieveSets(int offset, int length) {
if (offset > sets.size()) {
return new ListSetsResult(false, new ArrayList<Set>(), sets.size());
}
return new ListSetsResult(offset + length < sets.size(),
sets.subList(offset, min(offset + length, sets.size())), sets.size());
}
@Override
public boolean exists(String setSpec) {
for (Set set : sets) {
if (set.getSetSpec().equals(setSpec)) {
return true;
}
}
return false;
}
public StubbedSetRepository doesSupportSets() {
this.supports = true;
return this;
}
public StubbedSetRepository doesNotSupportSets() {
this.supports = false;
return this;
}
public StubbedSetRepository withSet(String name, String spec) {
this.sets.add(new Set(spec, name));
return this;
}
public StubbedSetRepository withRandomlyGeneratedSets(int number) {
for (int i = 0; i < number; i++) {
this.sets.add(new Set(randomAlphabetic(10), randomAlphabetic(10)));
}
return this;
}
public void clear() {
this.sets.clear();
}
}

View File

@@ -1,39 +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/
*/
package org.dspace.xoai.tests.helpers.stubs;
import com.lyncode.xoai.dataprovider.core.XOAIManager;
import com.lyncode.xoai.dataprovider.exceptions.ConfigurationException;
import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.Configuration;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.services.api.config.XOAIManagerResolverException;
import org.dspace.xoai.services.api.xoai.DSpaceFilterResolver;
import org.springframework.beans.factory.annotation.Autowired;
public class StubbedXOAIManagerResolver implements XOAIManagerResolver {
@Autowired
ResourceResolver resourceResolver;
@Autowired
DSpaceFilterResolver filterResolver;
private Configuration builder = new Configuration();
public Configuration configuration() {
return builder;
}
@Override
public XOAIManager getManager() throws XOAIManagerResolverException {
try {
return new XOAIManager(filterResolver, resourceResolver, builder);
} catch (ConfigurationException e) {
throw new XOAIManagerResolverException(e);
}
}
}

View File

@@ -1,186 +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/
*/
package org.dspace.xoai.tests.integration.xoai;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.xpath.XPathExpressionException;
import com.lyncode.builder.MapBuilder;
import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.services.impl.BaseDateProvider;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.Configuration;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.FormatConfiguration;
import org.apache.solr.client.solrj.SolrClient;
import org.dspace.xoai.controller.DSpaceOAIDataProvider;
import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.FieldResolver;
import org.dspace.xoai.services.api.config.ConfigurationService;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.tests.DSpaceTestConfiguration;
import org.dspace.xoai.tests.helpers.stubs.ItemRepositoryBuilder;
import org.dspace.xoai.tests.helpers.stubs.StubbedConfigurationService;
import org.dspace.xoai.tests.helpers.stubs.StubbedEarliestDateResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedFieldResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedResourceResolver;
import org.dspace.xoai.tests.helpers.stubs.StubbedSetRepository;
import org.dspace.xoai.tests.helpers.stubs.StubbedXOAIManagerResolver;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.result.XpathResultMatchers;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {DSpaceTestConfiguration.class, DSpaceOAIDataProvider.class})
public abstract class AbstractDSpaceTest {
private static final BaseDateProvider baseDateProvider = new BaseDateProvider();
@Autowired
WebApplicationContext wac;
private MockMvc mockMvc;
private StubbedXOAIManagerResolver xoaiManagerResolver;
private StubbedConfigurationService configurationService;
private StubbedFieldResolver databaseService;
private StubbedEarliestDateResolver earliestDateResolver;
private StubbedSetRepository setRepository;
private StubbedResourceResolver resourceResolver;
private SolrClient solrServer;
@Before
public void setup() {
xoaiManagerResolver = (StubbedXOAIManagerResolver) this.wac.getBean(XOAIManagerResolver.class);
configurationService = (StubbedConfigurationService) this.wac.getBean(ConfigurationService.class);
databaseService = (StubbedFieldResolver) this.wac.getBean(FieldResolver.class);
earliestDateResolver = (StubbedEarliestDateResolver) this.wac.getBean(EarliestDateResolver.class);
setRepository = this.wac.getBean(StubbedSetRepository.class);
setRepository.clear();
// solrServer = this.wac.getBean(SolrServer.class);
resourceResolver = (StubbedResourceResolver) this.wac.getBean(ResourceResolver.class);
xoaiManagerResolver.configuration();
}
@After
public void teardown() {
// Nullify all resources so that JUnit will clean them up
xoaiManagerResolver = null;
configurationService = null;
databaseService = null;
earliestDateResolver = null;
setRepository = null;
resourceResolver = null;
mockMvc = null;
solrServer = null;
}
protected MockMvc againstTheDataProvider() {
if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(this.wac).build();
}
return this.mockMvc;
}
protected Configuration theConfiguration() {
return xoaiManagerResolver.configuration();
}
protected StubbedConfigurationService theDSpaceConfiguration() {
return configurationService;
}
protected StubbedFieldResolver theDatabase() {
return databaseService;
}
protected StubbedEarliestDateResolver theEarlistEarliestDate() {
return earliestDateResolver;
}
protected XpathResultMatchers oaiXPath(String xpath) throws XPathExpressionException {
return MockMvcResultMatchers.xpath(this.replaceXpath(xpath), new MapBuilder<String, String>()
.withPair("o", "http://www.openarchives.org/OAI/2.0/")
.build());
}
private String replaceXpath(String xpath) {
int offset = 0;
String newXpath = "";
Pattern pattern = Pattern.compile("/[^/]+");
Matcher matcher = pattern.matcher(xpath);
while (matcher.find()) {
if (matcher.start() > offset) {
newXpath += xpath.substring(offset, matcher.start());
}
if (!matcher.group().contains(":") && !matcher.group().startsWith("/@")) {
newXpath += "/o:" + matcher.group().substring(1);
} else {
newXpath += matcher.group();
}
offset = matcher.end() + 1;
}
return newXpath;
}
protected String representationOfDate(Date date) {
return baseDateProvider.format(date);
}
protected StubbedSetRepository theSetRepository() {
return setRepository;
}
protected com.lyncode.xoai.dataprovider.xml.xoaiconfig.ContextConfiguration aContext(String baseUrl) {
return new com.lyncode.xoai.dataprovider.xml.xoaiconfig.ContextConfiguration(baseUrl);
}
protected XpathResultMatchers responseDate() throws XPathExpressionException {
return oaiXPath("/OAI-PMH/responseDate");
}
protected ResultMatcher verb(org.hamcrest.Matcher<? super String> matcher) throws XPathExpressionException {
return oaiXPath("/OAI-PMH/request/@verb").string(matcher);
}
protected XpathResultMatchers resumptionToken() throws XPathExpressionException {
return oaiXPath("//resumptionToken");
}
protected StubbedResourceResolver theResourseResolver() {
return resourceResolver;
}
protected FormatConfiguration aFormat(String id) {
return new FormatConfiguration(id);
}
protected ItemRepositoryBuilder.DSpaceItemBuilder anItem() {
return new ItemRepositoryBuilder.DSpaceItemBuilder();
}
private ItemRepositoryBuilder itemRepositoryBuilder;
public ItemRepositoryBuilder theItemRepository() {
if (itemRepositoryBuilder == null) {
itemRepositoryBuilder = new ItemRepositoryBuilder(solrServer);
}
return itemRepositoryBuilder;
}
}

View File

@@ -1,50 +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/
*/
package org.dspace.xoai.tests.integration.xoai;
import static org.dspace.xoai.tests.helpers.SyntacticSugar.and;
import static org.dspace.xoai.tests.helpers.SyntacticSugar.given;
import static org.hamcrest.core.Is.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Date;
import org.junit.Test;
public class IdentifyTest extends AbstractDSpaceTest {
public static final Date EARLIEST_DATE = new Date();
@Test
public void requestForIdentifyWithoutRequiredConfigurationAdminEmailSetShouldFail() throws Exception {
given(theDSpaceConfiguration()
.withoutProperty("mail.admin"));
and(given(theConfiguration().withContextConfigurations(aContext("request"))));
againstTheDataProvider().perform(get("/request?verb=Identify"))
.andExpect(status().isInternalServerError());
}
@Test
public void requestForIdentifyShouldReturnTheConfiguredValues() throws Exception {
given(theDSpaceConfiguration()
.hasProperty("dspace.name", "Test")
.hasProperty("mail.admin", "test@test.com"));
and(given(theEarlistEarliestDate().is(EARLIEST_DATE)));
and(given(theConfiguration().withContextConfigurations(aContext("request"))));
againstTheDataProvider().perform(get("/request?verb=Identify"))
.andExpect(status().isOk())
.andExpect(oaiXPath("//repositoryName").string("Test"))
.andExpect(oaiXPath("//adminEmail").string("test@test.com"))
.andExpect(
oaiXPath("//earliestDatestamp").string(is(representationOfDate(EARLIEST_DATE))));
}
}

View File

@@ -1,65 +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/
*/
package org.dspace.xoai.tests.integration.xoai;
import static org.dspace.xoai.tests.helpers.SyntacticSugar.and;
import static org.dspace.xoai.tests.helpers.SyntacticSugar.given;
import static org.hamcrest.core.Is.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
public class ListSetsTest extends AbstractDSpaceTest {
@Test
public void listSetsWithLessSetsThenMaxSetsPerPage() throws Exception {
given(theConfiguration()
.withMaxListSetsSize(100)
.withContextConfigurations(aContext("request")));
and(given(theSetRepository()
.doesSupportSets()
.withSet("name", "spec")));
againstTheDataProvider().perform(get("/request?verb=ListSets"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(responseDate().exists())
.andExpect(verb(is("ListSets")))
.andExpect(oaiXPath("//set").nodeCount(1))
.andExpect(oaiXPath("//set/setSpec").string("spec"))
.andExpect(oaiXPath("//set/setName").string("name"))
.andExpect(resumptionToken().doesNotExist());
}
@Test
public void listSetsWithMoreSetsThenMaxSetsPerPage() throws Exception {
given(theConfiguration()
.withMaxListSetsSize(10)
.withContextConfigurations(aContext("request")));
and(given(theSetRepository()
.doesSupportSets()
.withRandomlyGeneratedSets(20)));
againstTheDataProvider().perform(get("/request?verb=ListSets"))
.andExpect(status().isOk())
.andExpect(responseDate().exists())
.andExpect(verb(is("ListSets")))
.andExpect(oaiXPath("//set").nodeCount(10))
.andExpect(resumptionToken().string("////10"))
.andExpect(oaiXPath("//resumptionToken/@completeListSize").number(Double.valueOf(20)));
and(againstTheDataProvider().perform(get("/request?verb=ListSets&resumptionToken=////10"))
.andExpect(status().isOk())
.andExpect(responseDate().exists())
.andExpect(verb(is("ListSets")))
.andExpect(oaiXPath("//set").nodeCount(10))
.andExpect(resumptionToken().string("")));
}
}

View File

@@ -1,35 +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/
*/
package org.dspace.xoai.tests.integration.xoai;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
public class OAIContextTest extends AbstractDSpaceTest {
public static final String ROOT_URL = "/";
@Test
public void requestToRootShouldGiveListOfContextsWithBadRequestError() throws Exception {
againstTheDataProvider().perform(get(ROOT_URL))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(model().attributeExists("contexts"));
}
@Test
public void requestForUnknownContextShouldGiveListOfContextsWithBadRequestError() throws Exception {
againstTheDataProvider().perform(get("/unexistentContext"))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(model().attributeExists("contexts"));
}
}

View File

@@ -2,9 +2,9 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-rdf</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<name>DSpace RDF</name>
<description>Parent project for the RDF API and Webapp</description>
<description>DSpace RDF (Linked Data) Extension</description>
<parent>
<groupId>org.dspace</groupId>
@@ -57,20 +57,17 @@
<artifactId>dspace-services</artifactId>
</dependency>
<!-- Spring 3 dependencies -->
<!-- Needed to support Spring @Configuration classes (to register servlets/beans with Spring Boot webapp) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>

View File

@@ -0,0 +1,56 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RDF webapp configuration. Replaces the old web.xml
* <p>
* This @Configuration class is automatically discovered by Spring via a @ComponentScan.
* <p>
* All RDF web configurations (beans) can be enabled or disabled by setting "rdf.enabled"
* to true or false, respectively (in your DSpace configuration). Default is "false".
* <p>
* All @Value annotated configurations below can also be overridden in your DSpace configuration.
*
* @author Tim Donohue
*/
@Configuration
public class RDFWebConfig {
// Path where RDF should be deployed (when enabled). Defaults to "rdf"
@Value("${rdf.path:rdf}")
private String rdfPath;
// Servlet Beans. All of the below bean definitions map servlets to respond to specific URL patterns
// These are the combined equivalent of <servlet> and <servlet-mapping> in web.xml
// All beans are only loaded when rdf.enabled = true
@Bean
@ConditionalOnProperty("rdf.enabled")
public ServletRegistrationBean rdfSerializationBean() {
ServletRegistrationBean bean = new ServletRegistrationBean( new org.dspace.rdf.providing.DataProviderServlet(),
"/" + rdfPath + "/handle/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("rdf.enabled")
public ServletRegistrationBean rdfLocalURIRedirectionBean() {
ServletRegistrationBean bean = new ServletRegistrationBean(
new org.dspace.rdf.providing.LocalURIRedirectionServlet(), "/" + rdfPath + "/resource/*");
bean.setLoadOnStartup(1);
return bean;
}
}

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-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/
-->
<web-app id="DSpace-RDF" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>RDF Data Provider</display-name>
<context-param>
<description>
The location of the DSpace home directory
</description>
<param-name>dspace.dir</param-name>
<param-value>${dspace.dir}</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>${dspace.dir}/config/log4j2.xml</param-value>
<description>
The location of the Log4J configuration
</description>
</context-param>
<filter>
<filter-name>dspace.request</filter-name>
<filter-class>org.dspace.utils.servlet.DSpaceWebappServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>dspace.request</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.dspace.app.util.DSpaceContextListener</listener-class>
</listener>
<listener>
<listener-class>org.dspace.servicemanager.servlet.DSpaceKernelServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>rdf-serialization</servlet-name>
<servlet-class>org.dspace.rdf.providing.DataProviderServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>local-uri-redirection</servlet-name>
<servlet-class>org.dspace.rdf.providing.LocalURIRedirectionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>rdf-serialization</servlet-name>
<url-pattern>/handle/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>local-uri-redirection</servlet-name>
<url-pattern>/resource/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -27,9 +27,10 @@
<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> -->
<!-- Library for reading JSON documents: https://github.com/json-path/JsonPath -->
<json-path.version>2.4.0</json-path.version>
<!-- Library for managing JSON Web Tokens (JWT): https://bitbucket.org/connect2id/nimbus-jose-jwt/wiki/Home -->
<nimbus-jose-jwt.version>6.2</nimbus-jose-jwt.version>
</properties>
<profiles>
@@ -255,6 +256,26 @@
<groupId>org.dspace</groupId>
<artifactId>dspace-services</artifactId>
</dependency>
<!-- DSpace modules to deploy (this modules are all optional, but add features/endpoints to webapp) -->
<!-- You may choose to comment out any of these modules if you do not want/need its features -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-rdf</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
@@ -266,7 +287,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>6.2</version>
<version>${nimbus-jose-jwt.version}</version>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
@@ -288,13 +309,18 @@
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</exclusion>
<!-- More recent version used for testing below -->
<exclusion>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
<version>${json-path.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>

View File

@@ -14,6 +14,7 @@ import org.dspace.app.rest.filter.DSpaceRequestContextFilter;
import org.dspace.app.rest.model.hateoas.DSpaceRelProvider;
import org.dspace.app.rest.parameter.resolver.SearchFilterResolver;
import org.dspace.app.rest.utils.ApplicationConfig;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.dspace.app.util.DSpaceContextListener;
import org.dspace.utils.servlet.DSpaceWebappServletFilter;
@@ -22,7 +23,6 @@ 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;
@@ -62,7 +62,6 @@ public class Application extends SpringBootServletInitializer {
* This is necessary to allow us to build a deployable WAR, rather than
* always relying on embedded Tomcat.
* <p>
* <p>
* See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file
*
* @param application
@@ -70,13 +69,10 @@ public class Application extends SpringBootServletInitializer {
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// Pass this Application class, and our initializers for DSpace Kernel and Configuration
// NOTE: Kernel must be initialized before Configuration
return application.sources(Application.class)
.initializers(new DSpaceKernelInitializer());
}
@Bean
public ServletContextInitializer contextInitializer() {
return servletContext -> servletContext.setInitParameter("dspace.dir", configuration.getDspaceHome());
.initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer());
}
/**
@@ -89,7 +85,6 @@ public class Application extends SpringBootServletInitializer {
@Order(2)
protected DSpaceContextListener dspaceContextListener() {
// This listener initializes the DSpace Context object
// (and loads all DSpace configs)
return new DSpaceContextListener();
}

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.app.rest;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -19,6 +20,7 @@ import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.RelationshipRest;
import org.dspace.app.rest.model.RelationshipRestWrapper;
import org.dspace.app.rest.model.hateoas.RelationshipResourceWrapper;
import org.dspace.app.rest.repository.RelationshipRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.Item;
@@ -52,9 +54,17 @@ public class RelationshipRestController {
private static final String REGEX_REQUESTMAPPING_LABEL = "/{label:^(?!^\\d+$)" +
"(?!^[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}$)[\\w+\\-]+$+}";
/**
* Regular expression in the request mapping to accept number as identifier
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT = "/{id:\\d+}";
@Autowired
private RelationshipTypeService relationshipTypeService;
@Autowired
private RelationshipRestRepository relationshipRestRepository;
@Autowired
private RelationshipService relationshipService;
@@ -130,4 +140,29 @@ public class RelationshipRestController {
return relationshipResourceWrapper;
}
}
/**
* Method to change the left item of a relationship with a given item in the body
* @return The modified relationship
*/
@RequestMapping(method = RequestMethod.PUT, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT + "/leftItem",
consumes = {"text/uri-list"})
public RelationshipRest updateRelationshipLeft(@PathVariable Integer id, HttpServletResponse response,
HttpServletRequest request) throws SQLException {
Context context = ContextUtil.obtainContext(request);
return relationshipRestRepository.put(context,"/api/core/relationships/", id,
utils.getStringListFromRequest(request), false);
}
/**
* Method to change the right item of a relationship with a given item in the body
* @return The modified relationship
*/
@RequestMapping(method = RequestMethod.PUT, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT + "/rightItem",
consumes = {"text/uri-list"})
public RelationshipRest updateRelationshipRight(@PathVariable Integer id, HttpServletResponse response,
HttpServletRequest request) throws SQLException {
Context context = ContextUtil.obtainContext(request);
return relationshipRestRepository.put(context,"/api/core/relationships/", id,
utils.getStringListFromRequest(request), true);
}
}

View File

@@ -832,7 +832,7 @@ public class RestResourceController implements InitializingBean {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid)
.slash(subpath + '?' + querystring).withSelfRel();
} else {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid).withSelfRel();
link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath).withSelfRel();
}
Page<HALResource> halResources = pageResult.map(linkRepository::wrapResource);

View File

@@ -1,17 +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/
*/
package org.dspace.app.rest.exception;
/**
* This class creates an Exception to be used when the given DSpaceObjectType is invalid
*/
public class InvalidDSpaceObjectTypeException extends InvalidRequestException {
public InvalidDSpaceObjectTypeException(String message) {
super(message);
}
}

View File

@@ -1,21 +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/
*/
package org.dspace.app.rest.exception;
/**
* This class provides an exception for when the given request is invalid
*/
public class InvalidRequestException extends Exception {
public InvalidRequestException(String message) {
super(message);
}
public InvalidRequestException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,17 +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/
*/
package org.dspace.app.rest.exception;
/**
* Exception thrown when the search endpoint receives an invalid facet name
*/
public class InvalidSearchFacetException extends InvalidRequestException {
public InvalidSearchFacetException(final String message) {
super(message);
}
}

View File

@@ -1,21 +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/
*/
package org.dspace.app.rest.exception;
/**
* This class provides an exception to be used when the SearchFilter given is invalid
*/
public class InvalidSearchFilterException extends InvalidRequestException {
public InvalidSearchFilterException(String message, Throwable cause) {
super(message, cause);
}
public InvalidSearchFilterException(final String message) {
super(message);
}
}

View File

@@ -1,17 +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/
*/
package org.dspace.app.rest.exception;
/**
* This class makes an Exception to be used when a certain sorting is invalid
*/
public class InvalidSortingException extends InvalidRequestException {
public InvalidSortingException(String message) {
super(message);
}
}

View File

@@ -54,13 +54,13 @@ public class BrowseEntryHalLinkFactory extends HalLinkFactory<BrowseEntryResourc
return BrowseEntryResource.class;
}
// TODO use the reflaction to discover the link repository and additional information on the link annotation to
// TODO use the reflection to discover the link repository and additional information on the link annotation to
// build the parameters?
private UriComponentsBuilder addFilterParams(UriComponentsBuilder uriComponentsBuilder,
final BrowseEntryRest data) {
UriComponentsBuilder result;
if (data.getAuthority() != null) {
result = uriComponentsBuilder.queryParam("filterValue", data.getAuthority());
result = uriComponentsBuilder.queryParam("filterAuthority", data.getAuthority());
} else {
result = uriComponentsBuilder.queryParam("filterValue", data.getValue());
}

View File

@@ -17,7 +17,6 @@ import org.dspace.app.rest.converter.DiscoverFacetResultsConverter;
import org.dspace.app.rest.converter.DiscoverFacetsConverter;
import org.dspace.app.rest.converter.DiscoverResultConverter;
import org.dspace.app.rest.converter.DiscoverSearchSupportConverter;
import org.dspace.app.rest.exception.InvalidRequestException;
import org.dspace.app.rest.model.FacetConfigurationRest;
import org.dspace.app.rest.model.FacetResultsRest;
import org.dspace.app.rest.model.SearchConfigurationRest;
@@ -91,8 +90,7 @@ public class DiscoveryRestRepository extends AbstractDSpaceRestRepository {
public SearchResultsRest getSearchObjects(final String query, final String dsoType, final String dsoScope,
final String configuration,
final List<SearchFilter> searchFilters, final Pageable page)
throws InvalidRequestException {
final List<SearchFilter> searchFilters, final Pageable page) {
Context context = obtainContext();
IndexableObject scopeObject = scopeResolver.resolveScope(context, dsoScope);
DiscoveryConfiguration discoveryConfiguration = searchConfigurationService
@@ -131,8 +129,7 @@ public class DiscoveryRestRepository extends AbstractDSpaceRestRepository {
}
public FacetResultsRest getFacetObjects(String facetName, String prefix, String query, String dsoType,
String dsoScope, final String configuration, List<SearchFilter> searchFilters, Pageable page)
throws InvalidRequestException {
String dsoScope, final String configuration, List<SearchFilter> searchFilters, Pageable page) {
Context context = obtainContext();
@@ -158,7 +155,7 @@ public class DiscoveryRestRepository extends AbstractDSpaceRestRepository {
}
public SearchResultsRest getAllFacets(String query, String dsoType, String dsoScope, String configuration,
List<SearchFilter> searchFilters) throws InvalidRequestException {
List<SearchFilter> searchFilters) {
Context context = obtainContext();
Pageable page = new PageRequest(1, 1);

View File

@@ -34,6 +34,7 @@ import org.dspace.eperson.EPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;
@@ -128,29 +129,59 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
}
/*
* Disabled the put until https://jira.duraspace.org/browse/DS-4230 is discussed
@Override
protected RelationshipRest put(Context context, HttpServletRequest request, String apiCategory, String model,
Integer id, List<String> stringList)
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException {
/**
* Method to replace either the right or left item of a relationship with a given new item
* Called by request mappings in RelationshipRestController
* - For replace right item (itemToReplaceIsRight = true)
* => Newly proposed changed relationship: left = old-left; right = new-item
* - For replace left item (itemToReplaceIsRight = false)
* => Newly proposed changed relationship: left = new-item; right = old-right
* @param context
* @param contextPath What API call was made to get here
* @param id ID of the relationship we wish to modify
* @param stringList Item to replace either right or left item of relationship with
* @param itemToReplaceIsRight Boolean to decide whether to replace right item (true) or left item (false)
* @return The (modified) relationship
* @throws SQLException
*/
public RelationshipRest put(Context context, String contextPath, Integer id, List<String> stringList,
Boolean itemToReplaceIsRight) throws SQLException {
Relationship relationship = relationshipService.find(context, id);
Relationship relationship;
try {
relationship = relationshipService.find(context, id);
} catch (SQLException e) {
throw new ResourceNotFoundException(contextPath + " with id: " + id + " not found");
}
if (relationship == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
throw new ResourceNotFoundException(contextPath + " with id: " + id + " not found");
}
List<DSpaceObject> dSpaceObjects = utils.constructDSpaceObjectList(context, stringList);
if (dSpaceObjects.size() == 2 && dSpaceObjects.get(0).getType() == Constants.ITEM
&& dSpaceObjects.get(1).getType() == Constants.ITEM) {
Item leftItem = (Item) dSpaceObjects.get(0);
Item rightItem = (Item) dSpaceObjects.get(1);
if (dSpaceObjects.size() == 1 && dSpaceObjects.get(0).getType() == Constants.ITEM) {
Item replacementItemInRelationship = (Item) dSpaceObjects.get(0);
Item leftItem;
Item rightItem;
if (itemToReplaceIsRight) {
leftItem = relationship.getLeftItem();
rightItem = replacementItemInRelationship;
} else {
leftItem = replacementItemInRelationship;
rightItem = relationship.getRightItem();
}
if (isAllowedToModifyRelationship(context, relationship, leftItem, rightItem)) {
relationship.setLeftItem(leftItem);
relationship.setRightItem(rightItem);
relationshipService.updatePlaceInRelationship(context, relationship, false);
relationshipService.update(context, relationship);
try {
relationshipService.updatePlaceInRelationship(context, relationship, false);
relationshipService.update(context, relationship);
context.commit();
context.reloadEntity(relationship);
} catch (AuthorizeException e) {
throw new AccessDeniedException("You do not have write rights on this relationship's items");
}
return relationshipConverter.fromModel(relationship);
} else {
@@ -161,7 +192,6 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
}
}
*/
/**
* This method will check with the current user has write rights on both one of the original items and one of the
@@ -198,4 +228,4 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
log.error("Error deleting Relationship specified by ID:" + id, e);
}
}
}
}

View File

@@ -84,23 +84,24 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Logout configuration
.logout()
//On logout, clear the "session" salt
.addLogoutHandler(customLogoutHandler)
//Configure the logout entry point
.logoutRequestMatcher(new AntPathRequestMatcher("/api/authn/logout"))
//When logout is successful, return OK (204) status
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))
//Everyone can call this endpoint
.permitAll()
//On logout, clear the "session" salt
.addLogoutHandler(customLogoutHandler)
//Configure the logout entry point
.logoutRequestMatcher(new AntPathRequestMatcher("/api/authn/logout"))
//When logout is successful, return OK (204) status
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))
//Everyone can call this endpoint
.permitAll()
.and()
//Configure the URL patterns with their authentication requirements
.authorizeRequests()
//Allow POST by anyone on the login endpoint
.antMatchers(HttpMethod.POST,"/api/authn/login").permitAll()
//TRACE, CONNECT, OPTIONS, HEAD
//Everyone can call GET on the status endpoint
.antMatchers(HttpMethod.GET, "/api/authn/status").permitAll()
//Enable Spring Security authorization on /api/ URLs only
.antMatcher("/api/**").authorizeRequests()
//Allow POST by anyone on the login endpoint
.antMatchers(HttpMethod.POST,"/api/authn/login").permitAll()
//TRACE, CONNECT, OPTIONS, HEAD
//Everyone can call GET on the status endpoint
.antMatchers(HttpMethod.GET, "/api/authn/status").permitAll()
.and()
//Add a filter before our login endpoints to do the authentication based on the data in the HTTP request

View File

@@ -13,24 +13,24 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
/**
* This class provide extra configuration for our Spring Boot Application
* This class provides extra configuration for our Spring Boot Application
* <p>
* NOTE: @ComponentScan on "org.dspace.app.configuration" provides a way for other DSpace modules or plugins
* to "inject" their own Spring configurations / subpaths into our Spring Boot webapp.
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Tim Donohue
*/
@Configuration
@EnableSpringDataWebSupport
@ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils"})
@ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils",
"org.dspace.app.configuration"})
public class ApplicationConfig {
@Value("${dspace.dir}")
private String dspaceHome;
@Value("${cors.allowed-origins}")
// Allowed CORS origins. Defaults to * (everywhere)
// Can be overridden in DSpace configuration
@Value("${rest.cors.allowed-origins:*}")
private String corsAllowedOrigins;
public String getDspaceHome() {
return dspaceHome;
}
public String[] getCorsAllowedOrigins() {
if (corsAllowedOrigins != null) {
return corsAllowedOrigins.split("\\s*,\\s*");

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.utils;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.spring.ConfigurationPropertySource;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Utility class that will initialize the DSpace Configuration on Spring Boot startup.
* <P>
* NOTE: MUST be loaded after DSpaceKernelInitializer, as it requires the kernel is already initialized.
* <P>
* This initializer ensures that our DSpace Configuration is loaded into Spring's list of PropertySources
* very early in the Spring Boot startup process. That is important as it allows us to use DSpace configurations
* within @ConditionalOnProperty annotations on beans, as well as @Value annotations and XML bean definitions.
* <P>
* Used by org.dspace.app.rest.Application
*/
public class DSpaceConfigurationInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger log = LoggerFactory.getLogger(DSpaceConfigurationInitializer.class);
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
// Load DSpace Configuration service (requires kernel already initialized)
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
Configuration configuration = configurationService.getConfiguration();
// Create an Apache Commons Configuration Property Source from our configuration
ConfigurationPropertySource apacheCommonsConfigPropertySource =
new ConfigurationPropertySource(configuration.getClass().getName(), configuration);
// Append it to the Environment's list of PropertySources
applicationContext.getEnvironment().getPropertySources().addLast(apacheCommonsConfigPropertySource);
}
}

View File

@@ -11,6 +11,7 @@ import java.io.File;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.commons.lang3.StringUtils;
import org.dspace.kernel.DSpaceKernel;
import org.dspace.kernel.DSpaceKernelManager;
import org.dspace.servicemanager.DSpaceKernelImpl;
@@ -22,6 +23,7 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Utility class that will initialize the DSpace Kernel on Spring Boot startup.
@@ -35,16 +37,16 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
String dspaceHome = applicationContext.getEnvironment().getProperty("dspace.dir");
// Check if the kernel is already started
this.dspaceKernel = DSpaceKernelManager.getDefaultKernel();
if (this.dspaceKernel == null) {
DSpaceKernelImpl kernelImpl = null;
try {
// Load the kernel with default settings
kernelImpl = DSpaceKernelInit.getKernel(null);
if (!kernelImpl.isRunning()) {
kernelImpl.start(getProvidedHome(dspaceHome)); // init the kernel
// Determine configured DSpace home & init the Kernel
kernelImpl.start(getDSpaceHome(applicationContext.getEnvironment()));
}
this.dspaceKernel = kernelImpl;
@@ -65,8 +67,8 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
}
if (applicationContext.getParent() == null) {
//Set the DSpace Kernel Application context as a parent of the Spring Boot context so that
//we can auto-wire all DSpace Kernel services
// Set the DSpace Kernel Application context as a parent of the Spring Boot context so that
// we can auto-wire all DSpace Kernel services
applicationContext.setParent(dspaceKernel.getServiceManager().getApplicationContext());
//Add a listener for Spring Boot application shutdown so that we can nicely cleanup the DSpace kernel.
@@ -75,29 +77,35 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
}
/**
* Find DSpace's "home" directory.
* Find DSpace's "home" directory (from current environment)
* Initially look for JNDI Resource called "java:/comp/env/dspace.dir".
* If not found, look for "dspace.dir" initial context parameter.
* If not found, use value provided in "dspace.dir" in Spring Environment
*/
private String getProvidedHome(String dspaceHome) {
String providedHome = null;
private String getDSpaceHome(ConfigurableEnvironment environment) {
// Load the "dspace.dir" property from Spring Boot's Configuration (application.properties)
// This gives us the location of our DSpace configurations, necessary to start the kernel
String providedHome = environment.getProperty(DSpaceConfigurationService.DSPACE_HOME);
String dspaceHome = null;
try {
// Allow ability to override home directory via JNDI
Context ctx = new InitialContext();
providedHome = (String) ctx.lookup("java:/comp/env/" + DSpaceConfigurationService.DSPACE_HOME);
dspaceHome = (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);
// Otherwise, verify the 'providedHome' value is non-empty, exists and includes DSpace configs
if (dspaceHome == null) {
if (StringUtils.isNotBlank(providedHome) &&
!providedHome.equals("${" + DSpaceConfigurationService.DSPACE_HOME + "}")) {
File test = new File(providedHome);
if (test.exists() && new File(test, DSpaceConfigurationService.DSPACE_CONFIG_PATH).exists()) {
providedHome = dspaceHome;
dspaceHome = providedHome;
}
}
}
return providedHome;
return dspaceHome;
}

View File

@@ -16,11 +16,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.query.SearchQueryConverter;
import org.dspace.app.rest.exception.InvalidDSpaceObjectTypeException;
import org.dspace.app.rest.exception.InvalidRequestException;
import org.dspace.app.rest.exception.InvalidSearchFacetException;
import org.dspace.app.rest.exception.InvalidSearchFilterException;
import org.dspace.app.rest.exception.InvalidSortingException;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.parameter.SearchFilter;
import org.dspace.core.Constants;
import org.dspace.core.Context;
@@ -71,7 +67,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
DiscoveryConfiguration discoveryConfiguration,
String query, List<SearchFilter> searchFilters,
String dsoType, Pageable page)
throws InvalidRequestException {
throws DSpaceBadRequestException {
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
dsoType);
@@ -104,7 +100,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
DiscoveryConfiguration discoveryConfiguration,
String prefix, String query, List<SearchFilter> searchFilters,
String dsoType, Pageable page, String facetName)
throws InvalidRequestException {
throws DSpaceBadRequestException {
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
dsoType);
@@ -129,7 +125,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
private DiscoverQuery addFacetingForFacets(Context context, IndexableObject scope, String prefix,
DiscoverQuery queryArgs, DiscoveryConfiguration discoveryConfiguration, String facetName, Pageable page)
throws InvalidSearchFacetException {
throws DSpaceBadRequestException {
DiscoverySearchFilterFacet facet = discoveryConfiguration.getSidebarFacet(facetName);
if (facet != null) {
@@ -139,7 +135,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
fillFacetIntoQueryArgs(context, scope, prefix, queryArgs, facet, pageSize);
} else {
throw new InvalidSearchFacetException(facetName + " is not a valid search facet");
throw new DSpaceBadRequestException(facetName + " is not a valid search facet");
}
return queryArgs;
@@ -173,7 +169,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
private DiscoverQuery buildCommonDiscoverQuery(Context context, DiscoveryConfiguration discoveryConfiguration,
String query,
List<SearchFilter> searchFilters, String dsoType)
throws InvalidSearchFilterException, InvalidDSpaceObjectTypeException {
throws DSpaceBadRequestException {
DiscoverQuery queryArgs = buildBaseQueryForConfiguration(discoveryConfiguration);
//Add search filters
@@ -202,7 +198,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
private void configureSorting(Pageable page, DiscoverQuery queryArgs,
DiscoverySortConfiguration searchSortConfiguration) throws InvalidSortingException {
DiscoverySortConfiguration searchSortConfiguration) throws DSpaceBadRequestException {
String sortBy = null;
String sortOrder = null;
@@ -237,11 +233,11 @@ public class DiscoverQueryBuilder implements InitializingBean {
} else if ("desc".equalsIgnoreCase(sortOrder)) {
queryArgs.setSortField(sortField, DiscoverQuery.SORT_ORDER.desc);
} else {
throw new InvalidSortingException(sortOrder + " is not a valid sort order");
throw new DSpaceBadRequestException(sortOrder + " is not a valid sort order");
}
} else {
throw new InvalidSortingException(sortBy + " is not a valid sort field");
throw new DSpaceBadRequestException(sortBy + " is not a valid sort field");
}
}
@@ -273,16 +269,16 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
}
private int getDsoTypeId(String dsoType) throws InvalidDSpaceObjectTypeException {
private int getDsoTypeId(String dsoType) throws DSpaceBadRequestException {
int index = ArrayUtils.indexOf(Constants.typeText, dsoType.toUpperCase());
if (index < 0) {
throw new InvalidDSpaceObjectTypeException(dsoType + " is not a valid DSpace Object type");
throw new DSpaceBadRequestException(dsoType + " is not a valid DSpace Object type");
}
return index;
}
private String[] convertFilters(Context context, DiscoveryConfiguration discoveryConfiguration,
List<SearchFilter> searchFilters) throws InvalidSearchFilterException {
List<SearchFilter> searchFilters) throws DSpaceBadRequestException {
ArrayList<String> filterQueries = new ArrayList<>(CollectionUtils.size(searchFilters));
SearchQueryConverter searchQueryConverter = new SearchQueryConverter();
@@ -291,7 +287,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
for (SearchFilter searchFilter : CollectionUtils.emptyIfNull(transformedFilters)) {
DiscoverySearchFilter filter = discoveryConfiguration.getSearchFilter(searchFilter.getName());
if (filter == null) {
throw new InvalidSearchFilterException(searchFilter.getName() + " is not a valid search filter");
throw new DSpaceBadRequestException(searchFilter.getName() + " is not a valid search filter");
}
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
@@ -304,7 +300,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
}
} catch (SQLException e) {
throw new InvalidSearchFilterException("There was a problem parsing the search filters.", e);
throw new DSpaceBadRequestException("There was a problem parsing the search filters.", e);
}
return filterQueries.toArray(new String[filterQueries.size()]);

View File

@@ -7,34 +7,31 @@
#
#
# Spring Boot application.properties
# Docs (including info on how to override these default settings)
# Spring Boot's application.properties
#
# This properties file is used by Spring Boot to initialize its ApplicationContext and configure
# default Spring beans. It also uses the "dspace.dir" custom setting to locate your DSpace installation,
# and load all DSpace services and configurations.
#
# WARNING: Because this properties file initializes Spring Boot, it loads *before* any DSpace specific
# configurations/settings. Therefore settings in this file CANNOT depend on any DSpace configurations.
# The *only* DSpace configuration allowed in this file is "dspace.dir", which is documented below.
#
# 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:
# 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 home/installation directory
# REQUIRED to be specified in this application.properties file, as it is used to locate and initialize
# the DSpace Kernel and all Services (including configuration). See org.dspace.app.rest.Application.getDSpaceHome()
# NOTE: this configuration is filled out by Apache Ant during the DSpace install/update process. It does NOT
# interact with or read its configuration from dspace.cfg.
dspace.dir=${dspace.dir}
#dspace.dir=d:/install/dspace7
########################
# DSpace API CORS Settings
#
cors.allowed-origins = *
########################
# Spring Boot Settings
#
# Testing an "application Name"
spring.application.name = DSpace Spring Rest
########################
# Spring DATA Rest settings
@@ -75,15 +72,6 @@ server.port=8080
# (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
# Can be set to "never" if you don't want it.
@@ -94,8 +82,8 @@ server.error.include-stacktrace = always
#
# DISABLE a few autoconfiguration scripts, as DSpace initializes/configures these already
# * DataSourceAutoConfiguration (DB connection / datasource)
# * HibernateJpaAutoConfiguration (Hibernate ORM)
# * FlywayAutoConfiguration (Flyway migrations)
# * HibernateJpaAutoConfiguration (Hibernate)
# * SolrAutoConfiguration (Solr)
#
# TODO: At some point we may want to investigate whether we can re-enable these and remove the custom DSpace init code

View File

@@ -55,11 +55,11 @@
</head>
<body>
<div class="container">
<form class="form-signin">
<form id="login-form" class="form-signin">
<h2 class="form-signin-heading">HAL Browser</h2>
<input type="text" class="input-block-level" placeholder="Username" id="username">
<input type="password" class="input-block-level" placeholder="Password" id="password">
<button type="button" class="btn btn-large btn-primary" id="login">Sign in</button>
<button type="submit" class="btn btn-large btn-primary" id="login">Sign in</button>
<div class="other-login-methods hidden">
<h3>Other login methods:</h3>
@@ -133,13 +133,13 @@
return string.charAt(0).toUpperCase() + string.slice(1);
}
$("#login").click(function() {
$("#login-form").submit(function() {
$.ajax({
//This depends on this file to be called login.html
url : window.location.href.replace("login.html", "") + 'api/authn/login',
type : 'POST',
async : false,
data : 'password='+$("#password").val()+'&user='+$("#username").val() ,
data : 'password='+encodeURIComponent($("#password").val())+'&user='+encodeURIComponent($("#username").val()),
headers : {
"Content-Type" : 'application/x-www-form-urlencoded',
"Accept:" : '*/*'
@@ -149,6 +149,8 @@
toastr.error('The credentials you entered are invalid. Please try again.', 'Login Failed');
}
});
return false;
});
});
</script>

View File

@@ -0,0 +1,281 @@
/**
* 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.oai;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
import java.util.Date;
import com.lyncode.xoai.dataprovider.core.XOAIManager;
import com.lyncode.xoai.dataprovider.exceptions.ConfigurationException;
import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.services.impl.BaseDateProvider;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.Configuration;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.ContextConfiguration;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Community;
import org.dspace.services.ConfigurationService;
import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.cache.XOAICacheService;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.services.api.xoai.DSpaceFilterResolver;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
/**
* Integration test to verify the /oai endpoint is responding as a valid OAI-PMH endpoint.
* This tests that our dspace-oai module is running at this endpoint.
* <P>
* This is an AbstractControllerIntegrationTest because dspace-oai makes use of Controllers.
*
* @author Tim Donohue
*/
// Ensure the OAI SERVER IS ENABLED before any tests run.
// This annotation overrides default DSpace config settings loaded into Spring Context
@TestPropertySource(properties = {"oai.enabled = true"})
public class OAIpmhIT extends AbstractControllerIntegrationTest {
@Autowired
private ConfigurationService configurationService;
// All OAI-PMH paths that we test against
private final String ROOT_PATH = "/oai";
private final String DEFAULT_CONTEXT_PATH = "request";
private final String DEFAULT_CONTEXT = ROOT_PATH + "/" + DEFAULT_CONTEXT_PATH;
// Mock to ensure XOAI caching is disabled for all tests (see @Before method)
@MockBean
private XOAICacheService xoaiCacheService;
// Spy on the current EarliestDateResolver bean, to allow us to change behavior in tests below
@SpyBean
private EarliestDateResolver earliestDateResolver;
// XOAI's BaseDateProvider (used for date-based testing below)
private static BaseDateProvider baseDateProvider = new BaseDateProvider();
// Spy on the current XOAIManagerResolver bean, to allow us to change behavior of XOAIManager in tests
// See also: createMockXOAIManager() method
@SpyBean
private XOAIManagerResolver xoaiManagerResolver;
// Beans required by createMockXOAIManager()
@Autowired
private ResourceResolver resourceResolver;
@Autowired
private DSpaceFilterResolver filterResolver;
@Before
public void onlyRunIfConfigExists() {
// These integration tests REQUIRE that OAIWebConfig is found/available (as this class deploys OAI)
// If this class is not available, the below "Assume" will cause all tests to be SKIPPED
// NOTE: OAIWebConfig is provided by the 'dspace-oai' module
try {
Class.forName("org.dspace.app.configuration.OAIWebConfig");
} catch (ClassNotFoundException ce) {
Assume.assumeNoException(ce);
}
// Disable XOAI Caching for ALL tests
when(xoaiCacheService.isActive()).thenReturn(false);
when(xoaiCacheService.hasCache(anyString())).thenReturn(false);
}
@Test
public void requestToRootShouldGiveListOfContextsWithBadRequestError() throws Exception {
// Attempt to call the root endpoint
getClient().perform(get(ROOT_PATH))
// Expect a 400 response code (OAI requires a context)
.andExpect(status().isBadRequest())
// Expect that a list of valid contexts is returned
.andExpect(model().attributeExists("contexts"))
;
}
@Test
public void requestForUnknownContextShouldGiveListOfContextsWithBadRequestError() throws Exception {
// Attempt to call an nonexistent OAI-PMH context
getClient().perform(get(ROOT_PATH + "/nonexistentContext"))
// Expect a 400 response code (OAI requires a context)
.andExpect(status().isBadRequest())
// Expect that a list of valid contexts is returned
.andExpect(model().attributeExists("contexts"))
;
}
@Test
public void requestForIdentifyWithoutRequiredConfigShouldFail() throws Exception {
// Clear out the required "mail.admin" configuration
configurationService.setProperty("mail.admin", null);
// Attempt to make an Identify request to root context
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "Identify"))
// Expect a 500 response code (mail.admin MUST be set)
.andExpect(status().isInternalServerError())
;
}
@Test
public void requestForIdentifyShouldReturnTheConfiguredValues() throws Exception {
// Get current date/time and store as "now"
Date now = new Date();
// Return "now" when "getEarliestDate()" is called for the currently loaded EarliestDateResolver bean
doReturn(now).when(earliestDateResolver).getEarliestDate(context);
// Attempt to make an Identify request to root context
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "Identify"))
// Expect a 200 response code
.andExpect(status().isOk())
// Expect the content type to be "text/xml"
.andExpect(content().contentType("text/xml"))
// Expect <scheme>oai</scheme>
.andExpect(xpath("OAI-PMH/Identify/description/oai-identifier/scheme").string("oai"))
// Expect protocol version 2.0
.andExpect(xpath("OAI-PMH/Identify/protocolVersion").string("2.0"))
// Expect repositoryName to be the same as "dspace.name" config
.andExpect(xpath("OAI-PMH/Identify/repositoryName")
.string(configurationService.getProperty("dspace.name")))
// Expect adminEmail to be the same as "mail.admin" config
.andExpect(xpath("OAI-PMH/Identify/adminEmail")
.string(configurationService.getProperty("mail.admin")))
// Expect baseURL to be the same as our "oai.url" with the DEFAULT_CONTEXT_PATH appended
.andExpect(xpath("OAI-PMH/Identify/baseURL")
.string(configurationService.getProperty("oai.url") + "/" + DEFAULT_CONTEXT_PATH))
// Expect earliestDatestamp to be "now", i.e. current date, (as mocked above)
.andExpect(xpath("OAI-PMH/Identify/earliestDatestamp")
.string(baseDateProvider.format(now)))
;
}
@Test
public void listSetsWithLessSetsThenMaxSetsPerPage() throws Exception {
//Turn off the authorization system, otherwise we can't make the objects
context.turnOffAuthorisationSystem();
// Create a Community & a Collection
Community parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
CollectionBuilder.createCollection(context, parentCommunity)
.withName("Child Collection")
.build();
context.restoreAuthSystemState();
// Call ListSets verb, and verify both Collection & Community are listed as sets
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "ListSets"))
// Expect 200 response, with valid response date and verb=ListSets
.andExpect(status().isOk())
.andExpect(xpath("OAI-PMH/responseDate").exists())
.andExpect(xpath("OAI-PMH/request/@verb").string("ListSets"))
// Expect two Sets to be returned
.andExpect(xpath("//set").nodeCount(2))
// First setSpec should start with "com_" (Community)
.andExpect(xpath("(//set/setSpec)[1]").string(startsWith("com_")))
// First set name should be Community name
.andExpect(xpath("(//set/setName)[1]").string("Parent Community"))
// Second setSpec should start with "col_" (Collection)
.andExpect(xpath("(//set/setSpec)[2]").string(startsWith("col_")))
// Second set name should be Collection name
.andExpect(xpath("(//set/setName)[2]").string("Child Collection"))
// No resumption token should be returned
.andExpect(xpath("//resumptionToken").doesNotExist())
;
}
@Test
public void listSetsWithMoreSetsThenMaxSetsPerPage() throws Exception {
//Turn off the authorization system, otherwise we can't make the objects
context.turnOffAuthorisationSystem();
// Create 3 Communities (1 as a subcommunity) & 2 Collections
Community firstCommunity = CommunityBuilder.createCommunity(context)
.withName("First Community")
.build();
Community secondCommunity = CommunityBuilder.createSubCommunity(context, firstCommunity)
.withName("Second Community")
.build();
CommunityBuilder.createCommunity(context)
.withName("Third Community")
.build();
CollectionBuilder.createCollection(context, firstCommunity)
.withName("First Collection")
.build();
CollectionBuilder.createCollection(context, secondCommunity)
.withName("Second Collection")
.build();
context.restoreAuthSystemState();
// Create a custom XOAI configuration, with maxListSetsSize = 3 for DEFAULT_CONTEXT requests
// (This limits the number of sets returned in a single response)
Configuration xoaiConfig =
new Configuration().withMaxListSetsSize(3)
.withContextConfigurations(new ContextConfiguration(DEFAULT_CONTEXT_PATH));
// When xoaiManagerResolver.getManager() is called, return a MockXOAIManager based on the above configuration
doReturn(createMockXOAIManager(xoaiConfig)).when(xoaiManagerResolver).getManager();
// Call ListSets verb, and verify all 5 Collections/Communities are listed as sets
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "ListSets"))
// Expect 200 response, with valid response date and verb=ListSets
.andExpect(status().isOk())
.andExpect(xpath("OAI-PMH/responseDate").exists())
.andExpect(xpath("OAI-PMH/request/@verb").string("ListSets"))
// Expect ONLY 3 (of 5) Sets to be returned
.andExpect(xpath("//set").nodeCount(3))
// Expect resumption token to exist and be equal to "////3"
.andExpect(xpath("//resumptionToken").string("////3"))
// Expect resumption token to have completeListSize=5
.andExpect(xpath("//resumptionToken/@completeListSize").number(Double.valueOf(5)))
;
// Call ListSets verb, and verify all 5 Collections/Communities are listed as sets
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "ListSets"))
// Expect 200 response, with valid response date and verb=ListSets
.andExpect(status().isOk())
.andExpect(xpath("OAI-PMH/responseDate").exists())
.andExpect(xpath("OAI-PMH/request/@verb").string("ListSets"))
// Expect ONLY 3 (of 5) Sets to be returned
.andExpect(xpath("//set").nodeCount(3))
// Expect resumption token to exist and be equal to "////3"
.andExpect(xpath("//resumptionToken").string("////3"))
// Expect resumption token to have completeListSize=5
.andExpect(xpath("//resumptionToken/@completeListSize").number(Double.valueOf(5)))
;
}
/**
* Create a fake/mock XOAIManager class based on the given xoaiConfig. May be used by above tests
* to provide custom configurations to XOAI (overriding defaults in xoai.xml)
* @param xoaiConfig XOAI Configuration
* @return new XOAIManager initialized with the given Configuration
* @throws ConfigurationException
*/
private XOAIManager createMockXOAIManager(Configuration xoaiConfig) throws ConfigurationException {
return new XOAIManager(filterResolver, resourceResolver, xoaiConfig);
}
}

View File

@@ -0,0 +1,146 @@
/**
* 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.rdf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doReturn;
import java.net.URI;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.test.AbstractWebClientIntegrationTest;
import org.dspace.content.Community;
import org.dspace.content.service.SiteService;
import org.dspace.rdf.RDFUtil;
import org.dspace.rdf.conversion.RDFConverter;
import org.dspace.rdf.factory.RDFFactoryImpl;
import org.dspace.rdf.storage.RDFStorage;
import org.dspace.rdf.storage.RDFStorageImpl;
import org.dspace.services.ConfigurationService;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Spy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
/**
* Integration test to verify that the /rdf endpoint is responding as a valid RDF endpoint.
* This tests that our dspace-rdf module is running at this endpoint.
* <P>
* This is a AbstractWebClientIntegrationTest because testing dspace-rdf requires
* running a web server (as dspace-rdf makes use of Servlets, not Controllers).
* <P>
* NOTE: At this time, these ITs do NOT run against a real RDF triplestore. Instead,
* individual tests are expected to mock triplestore responses via a spy-able RDFStorage.
*
* @author Tim Donohue
*/
// Ensure the RDF endpoint IS ENABLED before any tests run.
// This annotation overrides default DSpace config settings loaded into Spring Context
@TestPropertySource(properties = {"rdf.enabled = true"})
public class RdfIT extends AbstractWebClientIntegrationTest {
@Autowired
private ConfigurationService configurationService;
@Autowired
private SiteService siteService;
@Autowired
private RDFConverter rdfConverter;
@Autowired
private RDFFactoryImpl rdfFactory;
// Create a new spy-able instance of RDFStorage. We will use this instance in all below tests (see @Before)
// so that we can fake a triplestore backend. No triplestore is used in these tests.
@Spy
RDFStorage rdfStorage = new RDFStorageImpl();
// All RDF paths that we test against
private final String SERIALIZE_PATH = "/rdf/handle";
private final String REDIRECTION_PATH = "/rdf/resource";
@Before
public void onlyRunIfConfigExists() {
// These integration tests REQUIRE that RDFWebConfig is found/available (as this class deploys RDF)
// If this class is not available, the below "Assume" will cause all tests to be SKIPPED
// NOTE: RDFWebConfig is provided by the 'dspace-rdf' module
try {
Class.forName("org.dspace.app.configuration.RDFWebConfig");
} catch (ClassNotFoundException ce) {
Assume.assumeNoException(ce);
}
// Change the running RDFFactory to use our spy-able, default instance of RDFStorage
// Again, this lets us fake a triplestore backend in individual tests below.
rdfFactory.setStorage(rdfStorage);
}
@Test
public void serializationTest() throws Exception {
//Turn off the authorization system, otherwise we can't make the objects
context.turnOffAuthorisationSystem();
// Create a Community
Community community = CommunityBuilder.createCommunity(context)
.withName("Test Community")
.build();
// Ensure Community is written to test DB immediately
context.commit();
context.restoreAuthSystemState();
// Get the RDF identifiers for this new Community & our Site object
String communityIdentifier = RDFUtil.generateIdentifier(context, community);
String siteIdentifier = RDFUtil.generateIdentifier(context, siteService.findSite(context));
// Mock an RDF triplestore's response by returning the RDF conversion of our Community
// when rdfStorage.load() is called with the RDF identifier for this Community
doReturn(rdfConverter.convert(context, community)).when(rdfStorage).load(communityIdentifier);
// Perform a GET request on the RDF /handle path, using our new Community's Handle
ResponseEntity<String> response = getResponseAsString(SERIALIZE_PATH + "/" + community.getHandle());
// Expect a 200 response code, and text/turtle (RDF Turtle syntax) response
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
assertThat(response.getHeaders().getContentType().toString(), equalTo("text/turtle;charset=UTF-8"));
// Turtle response should include the RDF identifier of Community
assertThat(response.getBody(), containsString(communityIdentifier));
// Turtle response should also note that this Community is part of our Site object
assertThat(response.getBody(), containsString("dspace:isPartOfRepository <" + siteIdentifier + "> ;"));
}
@Test
public void redirectionTest() throws Exception {
//Turn off the authorization system, otherwise we can't make the objects
context.turnOffAuthorisationSystem();
// Create a Community
Community community = CommunityBuilder.createCommunity(context)
.withName("Test Community")
.build();
// Ensure Community is written to test DB immediately (so that a lookup via handle will succeed below)
context.commit();
context.restoreAuthSystemState();
String communityHandle = community.getHandle();
// Perform a GET request on the RDF /resource path, using this Community's Handle
ResponseEntity<String> response = getResponseAsString(REDIRECTION_PATH + "/" + communityHandle);
// Expect a 303 (See Other) response code, redirecting us to the HTTP URI of the Community
assertThat(response.getStatusCode(), equalTo(HttpStatus.SEE_OTHER));
// Expect location of redirection to be [dspace.url]/handle/[community_handle]
assertThat(response.getHeaders().getLocation(), equalTo(
URI.create(configurationService.getProperty("dspace.url") + "/handle/" + communityHandle + "/")));
}
}

View File

@@ -387,6 +387,78 @@ public class BrowsesResourceControllerIT extends AbstractControllerIntegrationTe
not(matchMetadata("dc.title", "Internal publication")))));
}
@Test
/**
* This test was introduced to reproduce the bug DS-4269 Pagination links must be consistent also when there is not
* explicit pagination parameters in the request (i.e. defaults apply)
*
* @throws Exception
*/
public void browsePaginationWithoutExplicitParams() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community and one collection.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
//2. Twenty-one public items that are readable by Anonymous
for (int i = 0; i <= 20; i++) {
ItemBuilder.createItem(context, col1)
.withTitle("Public item " + String.format("%02d", i))
.withIssueDate("2017-10-17")
.withAuthor("Test, Author" + String.format("%02d", i))
.withSubject("Java").withSubject("Unit Testing")
.build();
}
context.restoreAuthSystemState();
//** WHEN **
//An anonymous user browses the items in the Browse by item endpoint
getClient().perform(get("/api/discover/browses/title/items"))
//** THEN **
//The status has to be 200 OK
.andExpect(status().isOk())
//We expect the content type to be "application/hal+json;charset=UTF-8"
.andExpect(content().contentType(contentType))
//We expect 21 public items
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(21)))
.andExpect(jsonPath("$.page.totalPages", is(2)))
.andExpect(jsonPath("$.page.number", is(0)))
// embedded items are already checked by other test, we focus on links here
.andExpect(jsonPath("$._links.next.href", Matchers.containsString("/api/discover/browses/title/items?")))
.andExpect(jsonPath("$._links.last.href", Matchers.containsString("/api/discover/browses/title/items?")))
.andExpect(
jsonPath("$._links.first.href", Matchers.containsString("/api/discover/browses/title/items?")))
.andExpect(jsonPath("$._links.self.href", Matchers.endsWith("/api/discover/browses/title/items")));
//** WHEN **
//An anonymous user browses the items in the Browse by item endpoint
getClient().perform(get("/api/discover/browses/author/entries"))
//** THEN **
//The status has to be 200 OK
.andExpect(status().isOk())
//We expect the content type to be "application/hal+json;charset=UTF-8"
.andExpect(content().contentType(contentType))
//We expect 21 public items
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(21)))
.andExpect(jsonPath("$.page.totalPages", is(2)))
.andExpect(jsonPath("$.page.number", is(0)))
// embedded items are already checked by other test, we focus on links here
.andExpect(jsonPath("$._links.next.href",
Matchers.containsString("/api/discover/browses/author/entries?")))
.andExpect(jsonPath("$._links.last.href",
Matchers.containsString("/api/discover/browses/author/entries?")))
.andExpect(jsonPath("$._links.first.href",
Matchers.containsString("/api/discover/browses/author/entries?")))
.andExpect(jsonPath("$._links.self.href", Matchers.endsWith("/api/discover/browses/author/entries")));
}
@Test
public void testPaginationBrowseByDateIssuedItems() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.app.rest;
import static com.jayway.jsonpath.JsonPath.read;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
import static org.hamcrest.Matchers.empty;
@@ -21,6 +22,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -37,6 +39,7 @@ import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
@@ -44,18 +47,22 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
/**
* Integration Tests against the /api/core/communities endpoint (including any subpaths)
*/
public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest {
@Autowired
CommunityConverter communityConverter;
@Autowired
CommunityService communityService;
@Autowired
AuthorizeService authorizeService;
@Test
public void createTest() throws Exception {
context.turnOffAuthorisationSystem();
ObjectMapper mapper = new ObjectMapper();
CommunityRest comm = new CommunityRest();
// We send a name but the created community should set this to the title
@@ -86,41 +93,54 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
comm.setMetadata(metadataRest);
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken).perform(post("/api/core/communities")
// Capture the UUID of the created Community (see andDo() below)
AtomicReference<UUID> idRef = new AtomicReference<UUID>();
try {
getClient(authToken).perform(post("/api/core/communities")
.content(mapper.writeValueAsBytes(comm))
.contentType(contentType))
.andExpect(status().isCreated())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.id", not(empty())),
hasJsonPath("$.uuid", not(empty())),
hasJsonPath("$.name", is("Title Text")),
hasJsonPath("$.handle", not(empty())),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._links.collections.href", not(empty())),
hasJsonPath("$._links.logo.href", not(empty())),
hasJsonPath("$._links.subcommunities.href", not(empty())),
hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf(
matchMetadata("dc.description", "<p>Some cool HTML code here</p>"),
matchMetadata("dc.description.abstract",
.andExpect(status().isCreated())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.id", not(empty())),
hasJsonPath("$.uuid", not(empty())),
hasJsonPath("$.name", is("Title Text")),
hasJsonPath("$.handle", not(empty())),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._links.collections.href", not(empty())),
hasJsonPath("$._links.logo.href", not(empty())),
hasJsonPath("$._links.subcommunities.href", not(empty())),
hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf(
matchMetadata("dc.description", "<p>Some cool HTML code here</p>"),
matchMetadata("dc.description.abstract",
"Sample top-level community created via the REST API"),
matchMetadata("dc.description.tableofcontents", "<p>HTML News</p>"),
matchMetadata("dc.rights", "Custom Copyright Text"),
matchMetadata("dc.title", "Title Text")
)))));
matchMetadata("dc.description.tableofcontents", "<p>HTML News</p>"),
matchMetadata("dc.rights", "Custom Copyright Text"),
matchMetadata("dc.title", "Title Text")
)
)
)))
// capture "id" returned in JSON response
.andDo(result -> idRef
.set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id"))));
} finally {
// Delete the created community (cleanup after ourselves!)
CommunityBuilder.deleteCommunity(idRef.get());
}
}
@Test
public void createWithParentTest() throws Exception {
context.turnOffAuthorisationSystem();
//We turn off the authorization system in order to create the structure as defined below
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and one collection.
// Create a parent community to POST a new sub-community to
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper();
CommunityRest comm = new CommunityRest();
@@ -140,23 +160,26 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken).perform(post("/api/core/communities")
// Capture the UUID of the created Community (see andDo() below)
AtomicReference<UUID> idRef = new AtomicReference<UUID>();
try {
getClient(authToken).perform(post("/api/core/communities")
.content(mapper.writeValueAsBytes(comm))
.param("parent", parentCommunity.getID().toString())
.contentType(contentType))
.andExpect(status().isCreated())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.id", not(empty())),
hasJsonPath("$.uuid", not(empty())),
hasJsonPath("$.name", is("Title Text")),
hasJsonPath("$.handle", not(empty())),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._links.collections.href", not(empty())),
hasJsonPath("$._links.logo.href", not(empty())),
hasJsonPath("$._links.subcommunities.href", not(empty())),
hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf(
.andExpect(status().isCreated())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.id", not(empty())),
hasJsonPath("$.uuid", not(empty())),
hasJsonPath("$.name", is("Title Text")),
hasJsonPath("$.handle", not(empty())),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._links.collections.href", not(empty())),
hasJsonPath("$._links.logo.href", not(empty())),
hasJsonPath("$._links.subcommunities.href", not(empty())),
hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf(
MetadataMatcher.matchMetadata("dc.description",
"<p>Some cool HTML code here</p>"),
MetadataMatcher.matchMetadata("dc.description.abstract",
@@ -167,13 +190,18 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
"Custom Copyright Text"),
MetadataMatcher.matchMetadata("dc.title",
"Title Text")
)))));
)
)
)))
// capture "id" returned in JSON response
.andDo(result -> idRef
.set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id"))));
} finally {
// Delete the created community (cleanup after ourselves!)
CommunityBuilder.deleteCommunity(idRef.get());
}
}
@Test
public void createUnauthorizedTest() throws Exception {
context.turnOffAuthorisationSystem();
@@ -854,6 +882,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
}
@Test
public void patchCommunityMetadataAuthorized() throws Exception {
runPatchMetadataTests(admin, 200);
}

View File

@@ -1768,6 +1768,12 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
new MetadataPatchSuite().runWith(getClient(token), "/api/core/items/" + item.getID(), expectedStatus);
}
/**
* This test will try creating an item with the InArchive property set to false. This endpoint does not allow
* us to create Items which aren't final (final means that they'd be in archive) and thus it'll throw a
* BadRequestException which is what we're testing for
* @throws Exception If something goes wrong
*/
@Test
public void testCreateItemInArchiveFalseBadRequestException() throws Exception {

View File

@@ -45,7 +45,6 @@ import org.dspace.content.service.RelationshipTypeService;
import org.dspace.core.Constants;
import org.dspace.core.I18nUtil;
import org.dspace.eperson.EPerson;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@@ -195,7 +194,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -269,7 +267,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1420,7 +1417,14 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
}
@Ignore
/**
* This test will create a relationship with author 1 - publication 1
* Then modify this relationship by changing the left item to author 2 via PUT > Verify
* Then modify this relationship by changing the right item to publication 2 via PUT > Verify
*
* @throws Exception
*/
@Test
public void putRelationshipAdminAccess() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1432,7 +1436,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1456,6 +1459,13 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withRelationshipType("Publication")
.build();
Item publication2 = ItemBuilder.createItem(context, col3)
.withTitle("Publication2")
.withAuthor("Testy, TEst")
.withIssueDate("2015-01-01")
.withRelationshipType("Publication")
.build();
RelationshipType isAuthorOfPublicationRelationshipType = relationshipTypeService
.findbyTypesAndLabels(context, entityTypeService.findByEntityType(context, "Publication"),
entityTypeService.findByEntityType(context, "Person"),
@@ -1485,19 +1495,37 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType
(org.springframework.data.rest.webmvc.RestMediaTypes
.TEXT_URI_LIST_VALUE))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//Modify the left item in the relationship publication > publication 2
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/leftItem")
.contentType(MediaType.parseMediaType
(org.springframework.data.rest.webmvc.RestMediaTypes
.TEXT_URI_LIST_VALUE))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify left item change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())));
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
//Modify the right item in the relationship publication > publication 2
MvcResult mvcResult3 = getClient(token).perform(put("/api/core/relationships/" + id + "/rightItem")
.contentType(MediaType.parseMediaType
(org.springframework.data.rest.webmvc.RestMediaTypes
.TEXT_URI_LIST_VALUE))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify right item change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())))
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())));
}
@@ -1506,7 +1534,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 1 and author 2
* Verify this is possible for a user with WRITE permissions on author 1 and author 2
*/
@Ignore
@Test
public void putRelationshipWriteAccessOnAuthors() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1518,7 +1546,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1580,17 +1607,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//change right item from author 1 > author 2
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/rightItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())));
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())))
.andExpect(jsonPath("$.leftId", is(publication.getID().toString())));
}
@@ -1599,7 +1626,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 1 and author 2
* Verify this is possible for a user with WRITE permissions on publication 1
*/
@Ignore
@Test
public void putRelationshipWriteAccessOnPublication() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1611,7 +1638,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1671,18 +1697,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
String content = mvcResult.getResponse().getContentAsString();
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//change rightItem from author1 > author2
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/rightItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify right item change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())));
.andExpect(jsonPath("$.rightId", is(author2.getID().toString())))
.andExpect(jsonPath("$.leftId", is(publication.getID().toString())));
}
@@ -1692,7 +1717,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 2 and author 1
* Verify this is possible for a user with WRITE permissions on publication 1 and publication 2
*/
@Ignore
@Test
public void putRelationshipWriteAccessOnPublications() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1704,7 +1729,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1714,13 +1738,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withRelationshipType("Person")
.build();
Item author2 = ItemBuilder.createItem(context, col1)
.withTitle("Author2")
.withIssueDate("2017-10-12")
.withAuthor("Smith, Donalaze")
.withRelationshipType("Person")
.build();
Item publication1 = ItemBuilder.createItem(context, col3)
.withTitle("Publication1")
.withAuthor("Testy, TEst")
@@ -1771,19 +1788,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
String content = mvcResult.getResponse().getContentAsString();
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication2.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author1.getID()))
.andExpect(status().isOk())
.andReturn();
//change leftItem
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/leftItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + publication2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())));
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
}
@@ -1792,7 +1807,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 2 and author 1
* Verify this is possible for a user with WRITE permissions on author 1
*/
@Ignore
@Test
public void putRelationshipWriteAccessOnAuthor() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1804,7 +1819,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1814,13 +1828,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withRelationshipType("Person")
.build();
Item author2 = ItemBuilder.createItem(context, col1)
.withTitle("Author2")
.withIssueDate("2017-10-12")
.withAuthor("Smith, Donalaze")
.withRelationshipType("Person")
.build();
Item publication1 = ItemBuilder.createItem(context, col3)
.withTitle("Publication1")
.withAuthor("Testy, TEst")
@@ -1872,19 +1879,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
String content = mvcResult.getResponse().getContentAsString();
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication2.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author1.getID()))
.andExpect(status().isOk())
.andReturn();
//change left item
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/leftItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + publication2.getID()))
.andExpect(status().isOk())
.andReturn();
//verify change and other not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())));
.andExpect(jsonPath("$.leftId", is(publication2.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
}
@@ -1893,7 +1898,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 1 and author 2
* Verify this is NOT possible for a user without WRITE permissions
*/
@Ignore
@Test
public void putRelationshipNoAccess() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1905,7 +1910,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -1964,15 +1968,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
token = getAuthToken(user.getEmail(), password);
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isForbidden())
.andReturn();
//attempt change, expect not allowed
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/rightItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isForbidden())
.andReturn();
//verify nothing changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
}
/**
@@ -1980,7 +1986,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 1 and author 2
* Verify this is NOT possible for a user with WRITE permissions on author 1
*/
@Ignore
@Test
public void putRelationshipOnlyAccessOnOneAuthor() throws Exception {
context.turnOffAuthorisationSystem();
@@ -2053,15 +2059,17 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
token = getAuthToken(user.getEmail(), password);
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isForbidden())
.andReturn();
//attempt right item change, expect not allowed
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/rightItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + author2.getID()))
.andExpect(status().isForbidden())
.andReturn();
//verify not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
}
/**
@@ -2069,7 +2077,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
* Change it to a relationship between publication 2 and author 1
* Verify this is NOT possible for a user with WRITE permissions on publication 1
*/
@Ignore
@Test
public void putRelationshipOnlyAccessOnOnePublication() throws Exception {
context.turnOffAuthorisationSystem();
@@ -2081,7 +2089,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
Collection col3 = CollectionBuilder.createCollection(context, child1).withName("OrgUnits").build();
Item author1 = ItemBuilder.createItem(context, col1)
@@ -2091,13 +2098,6 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
.withRelationshipType("Person")
.build();
Item author2 = ItemBuilder.createItem(context, col1)
.withTitle("Author2")
.withIssueDate("2017-10-12")
.withAuthor("Smith, Donalaze")
.withRelationshipType("Person")
.build();
Item publication1 = ItemBuilder.createItem(context, col3)
.withTitle("Publication1")
.withAuthor("Testy, TEst")
@@ -2147,18 +2147,109 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
String content = mvcResult.getResponse().getContentAsString();
Map<String,Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id)
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication2.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author1.getID()))
.andExpect(status().isForbidden())
.andReturn();
//attempt left item change, expect not allowed
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/leftItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + publication2.getID()))
.andExpect(status().isForbidden())
.andReturn();
//verify not changed
getClient(token).perform(get("/api/core/relationships/" + id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.leftId", is(publication1.getID().toString())));
.andExpect(jsonPath("$.leftId", is(publication1.getID().toString())))
.andExpect(jsonPath("$.rightId", is(author1.getID().toString())));
}
}
@Test
public void putRelationshipWithNonexistentID() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Item publication1 = ItemBuilder.createItem(context, col1)
.withTitle("Publication1")
.withAuthor("Testy, TEst")
.withIssueDate("2015-01-01")
.withRelationshipType("Publication")
.build();
String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
int nonexistentRelationshipID = 404404404;
//attempt left item change on non-existent relationship
MvcResult mvcResult = getClient(token).perform(
put("/api/core/relationships/" + nonexistentRelationshipID + "/leftItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + publication1.getID()))
.andExpect(status().isNotFound())
.andReturn();
}
@Test
public void putRelationshipWithInvalidItemIDInBody() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Item author1 = ItemBuilder.createItem(context, col1)
.withTitle("Author1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald")
.withRelationshipType("Person")
.build();
Item publication1 = ItemBuilder.createItem(context, col1)
.withTitle("Publication1")
.withAuthor("Testy, TEst")
.withIssueDate("2015-01-01")
.withRelationshipType("Publication")
.build();
RelationshipType isAuthorOfPublicationRelationshipType = relationshipTypeService
.findbyTypesAndLabels(context, entityTypeService.findByEntityType(context, "Publication"),
entityTypeService.findByEntityType(context, "Person"),
"isAuthorOfPublication", "isPublicationOfAuthor");
String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
MvcResult mvcResult = getClient(token).perform(post("/api/core/relationships")
.param("relationshipType",
isAuthorOfPublicationRelationshipType.getID()
.toString())
.contentType(MediaType.parseMediaType("text/uri-list"))
.content(
"https://localhost:8080/spring-rest/api/core/items/" + publication1.getID() + "\n" +
"https://localhost:8080/spring-rest/api/core/items/" + author1.getID()))
.andExpect(status().isCreated())
.andReturn();
ObjectMapper mapper = new ObjectMapper();
String content = mvcResult.getResponse().getContentAsString();
Map<String, Object> map = mapper.readValue(content, Map.class);
String id = String.valueOf(map.get("id"));
int nonexistentItemID = 404404404;
//attempt left item change on non-existent relationship
MvcResult mvcResult2 = getClient(token).perform(put("/api/core/relationships/" + id + "/leftItem")
.contentType(MediaType.parseMediaType("text/uri-list"))
.content("https://localhost:8080/spring-rest/api/core/items/" + nonexistentItemID))
.andExpect(status().isUnprocessableEntity())
.andReturn();
}
}

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest.builder;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.UUID;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
@@ -92,4 +93,26 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder<Community> {
protected DSpaceObjectService<Community> getService() {
return communityService;
}
/**
* Delete the Test Community referred to by the given UUID
* @param uuid UUID of Test Community to delete
* @throws SQLException
* @throws IOException
*/
public static void deleteCommunity(UUID uuid) throws SQLException, IOException {
try (Context c = new Context()) {
c.turnOffAuthorisationSystem();
Community community = communityService.find(c, uuid);
if (community != null) {
try {
communityService.delete(c, community);
} catch (AuthorizeException e) {
// cannot occur, just wrap it to make the compiler happy
throw new RuntimeException(e.getMessage(), e);
}
}
c.complete();
}
}
}

View File

@@ -22,6 +22,7 @@ import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.Application;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.junit.Assert;
import org.junit.runner.RunWith;
@@ -44,11 +45,18 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.web.context.WebApplicationContext;
/**
* Abstract controller integration test class that will take care of setting up the
* Spring Boot environment to run the integration test
* Abstract integration test class that will take care of setting up the Spring Boot environment to run
* integration tests against @Controller classes (Spring Controllers).
* <P>
* This Abstract class uses Spring Boot's default mock environment testing scheme, which relies on MockMvc to "mock"
* a webserver and call Spring Controllers directly. This avoids the cost of starting a webserver.
* <P>
* If you need to test a Servlet (or something not a Spring Controller), you will NOT be able to use this class.
* Instead, please use the AbstractWebClientIntegrationTest in this same package.
*
* @author Tom Desair
* @author Tim Donohue
* @see org.dspace.app.rest.test.AbstractWebClientIntegrationTest
*/
// Run tests with JUnit 4 and Spring TestContext Framework
@RunWith(SpringRunner.class)
@@ -56,8 +64,8 @@ import org.springframework.web.context.WebApplicationContext;
// NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
// See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
@SpringBootTest(classes = Application.class)
// Load DSpaceKernelInitializer in Spring ApplicationContext (to initialize DSpace Kernel)
@ContextConfiguration(initializers = DSpaceKernelInitializer.class)
// Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
// Tell Spring to make ApplicationContext an instance of WebApplicationContext (for web-based tests)
@WebAppConfiguration
public class AbstractControllerIntegrationTest extends AbstractIntegrationTestWithDatabase {

View File

@@ -184,6 +184,9 @@ public class AbstractIntegrationTestWithDatabase extends AbstractDSpaceIntegrati
.getServiceByName(SearchService.class.getName(), MockSolrServiceImpl.class);
searchService.reset();
// Reload our ConfigurationService (to reset configs to defaults again)
DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig();
// NOTE: we explicitly do NOT destroy our default eperson & admin as they
// are cached and reused for all tests. This speeds up all tests.
} catch (Exception e) {

View File

@@ -0,0 +1,118 @@
/**
* 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.test;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.Application;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Abstract web client integration test class that will initialize the Spring Boot test environment by starting up
* a full test webserver (on a random port).
* <P>
* As running a test webserver is an expensive operation, this Abstract class is only necessary to perform
* Integration Tests on *Servlets*. If you are performing integration tests on a Spring Controller
* (@Controller annotation), you should use AbstractControllerIntegrationTest
* <P>
* NOTE: The annotations on this class should be kept in sync with those on AbstractControllerIntegrationTest.
* The ONLY differences should be in the "webEnvironment" param passed to @SpringBootTest, and the removal
* of @WebAppConfiguration (which is only allowed in a mock environment)
*
* @author Tim Donohue
* @see org.dspace.app.rest.test.AbstractControllerIntegrationTest
*/
// Run tests with JUnit 4 and Spring TestContext Framework
@RunWith(SpringRunner.class)
// Specify main class to use to load Spring ApplicationContext
// ALSO tell Spring to start a web server on a random port
// NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
// See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWithDatabase {
// (Random) port chosen for test web server
@LocalServerPort
private int port;
// RestTemplate class with access to test web server
@Autowired
private TestRestTemplate restTemplate;
// Spring Application context
@Autowired
protected ApplicationContext applicationContext;
/**
* Get client TestRestTemplate for making HTTP requests to test webserver
* @return TestRestTemplate
*/
public TestRestTemplate getClient() {
return restTemplate;
}
/**
* Return the full URL of a request at a specific path.
* (http://localhost:[port][path])
* @param path Path (should start with a slash)
* @return full URL
*/
public String getURL(String path) {
return "http://localhost:" + port + path;
}
/**
* Perform a GET request and return response as a String
* @param path path to perform GET against
* @return ResponseEntity with a String body
*/
public ResponseEntity<String> getResponseAsString(String path) {
return getClient().getForEntity(getURL(path), String.class);
}
/**
* Perform an authenticated (via Basic Auth) GET request and return response as a String
* @param path path to perform GET against
* @param username Username
* @param password Password
* @return ResponseEntity with a String body
*/
public ResponseEntity<String> getResponseAsString(String path, String username, String password) {
return getClient().withBasicAuth(username, password).getForEntity(getURL(path), String.class);
}
/**
* Perform an authenticated (via Basic Auth) POST request and return response as a String.
* @param path path to perform GET against
* @param username Username (may be null to perform an unauthenticated POST)
* @param password Password
* @return ResponseEntity with a String body
*/
public ResponseEntity<String> postResponseAsString(String path, String username, String password,
HttpEntity requestEntity) {
// If username is not empty, perform an authenticated POST. Else attempt without AuthN
if (StringUtils.isNotBlank(username)) {
return getClient().withBasicAuth(username, password).postForEntity(getURL(path), requestEntity,
String.class);
} else {
return getClient().postForEntity(getURL(path), requestEntity, String.class);
}
}
}

View File

@@ -23,10 +23,7 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.dspace.app.rest.exception.InvalidDSpaceObjectTypeException;
import org.dspace.app.rest.exception.InvalidSearchFacetException;
import org.dspace.app.rest.exception.InvalidSearchFilterException;
import org.dspace.app.rest.exception.InvalidSortingException;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.parameter.SearchFilter;
import org.dspace.core.Constants;
import org.dspace.core.Context;
@@ -245,20 +242,20 @@ public class DiscoverQueryBuilderTest {
new ReflectionEquals(new DiscoverHitHighlightingField("fulltext", 0, 3))));
}
@Test(expected = InvalidDSpaceObjectTypeException.class)
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidDSOType() throws Exception {
queryBuilder
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "TEST", page);
}
@Test(expected = InvalidSortingException.class)
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidSortField() throws Exception {
page = new PageRequest(2, 10, Sort.Direction.ASC, "test");
queryBuilder
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "ITEM", page);
}
@Test(expected = InvalidSearchFilterException.class)
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidSearchFilter1() throws Exception {
searchFilter = new SearchFilter("test", "equals", "Smith, Donald");
@@ -266,7 +263,7 @@ public class DiscoverQueryBuilderTest {
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "ITEM", page);
}
@Test(expected = InvalidSearchFilterException.class)
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidSearchFilter2() throws Exception {
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class)))
.thenThrow(SQLException.class);
@@ -297,7 +294,7 @@ public class DiscoverQueryBuilderTest {
));
}
@Test(expected = InvalidSearchFacetException.class)
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidSearchFacet() throws Exception {
queryBuilder.buildFacetQuery(context, scope, discoveryConfiguration, null, query,
Arrays.asList(searchFilter), "item", page, "test");

View File

@@ -0,0 +1,117 @@
/**
* 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.sword;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import org.dspace.app.rest.test.AbstractWebClientIntegrationTest;
import org.dspace.services.ConfigurationService;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
/**
* Integration test to verify that the /sword endpoint is responding as a valid SWORD endpoint.
* This tests that our dspace-sword module is running at this endpoint.
* <P>
* This is a AbstractWebClientIntegrationTest because testing dspace-sword requires
* running a web server (as dspace-sword makes use of Servlets, not Controllers).
*
* @author Tim Donohue
*/
// Ensure the SWORD SERVER IS ENABLED before any tests run.
// This annotation overrides default DSpace config settings loaded into Spring Context
@TestPropertySource(properties = {"sword-server.enabled = true"})
public class Swordv1IT extends AbstractWebClientIntegrationTest {
@Autowired
private ConfigurationService configurationService;
// All SWORD paths that we test against
private final String SERVICE_DOC_PATH = "/sword/servicedocument";
private final String DEPOSIT_PATH = "/sword/deposit";
private final String MEDIA_LINK_PATH = "/sword/media-link";
@Before
public void onlyRunIfConfigExists() {
// These integration tests REQUIRE that SWORDWebConfig is found/available (as this class deploys SWORD)
// If this class is not available, the below "Assume" will cause all tests to be SKIPPED
// NOTE: SWORDWebConfig is provided by the 'dspace-sword' module
try {
Class.forName("org.dspace.app.configuration.SWORDWebConfig");
} catch (ClassNotFoundException ce) {
Assume.assumeNoException(ce);
}
// Ensure SWORD URL configurations are set correctly (based on our integration test server's paths)
// SWORD validates requests against these configs, and throws a 404 if they don't match the request path
configurationService.setProperty("sword-server.servicedocument.url", getURL(SERVICE_DOC_PATH));
configurationService.setProperty("sword-server.deposit.url", getURL(DEPOSIT_PATH));
configurationService.setProperty("sword-server.media-link.url", getURL(MEDIA_LINK_PATH));
}
@Test
public void serviceDocumentUnauthorizedTest() throws Exception {
// Attempt to load the ServiceDocument without first authenticating
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
public void serviceDocumentTest() throws Exception {
// Attempt to load the ServiceDocument as an Admin user.
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH,
admin.getEmail(), password);
// Expect a 200 response code, and an ATOM UTF-8 document
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
assertThat(response.getHeaders().getContentType().toString(), equalTo("application/atomsvc+xml;charset=UTF-8"));
// Check for SWORD version in response body
assertThat(response.getBody(), containsString("<sword:version>1.3</sword:version>"));
}
@Test
public void depositUnauthorizedTest() throws Exception {
// Attempt to access /deposit endpoint without sending authentication information
ResponseEntity<String> response = postResponseAsString(DEPOSIT_PATH, null, null, null);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void depositTest() throws Exception {
// TODO: Actually test a full deposit via SWORD.
// Currently, we are just ensuring the /deposit endpoint exists (see above) and isn't throwing a 404
}
@Test
public void mediaLinkUnauthorizedTest() throws Exception {
// Attempt to access /media-link endpoint without sending authentication information
ResponseEntity<String> response = getResponseAsString(MEDIA_LINK_PATH);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void mediaLinkTest() throws Exception {
// TODO: Actually test a /media-link request.
// Currently, we are just ensuring the /media-link endpoint exists (see above) and isn't throwing a 404
}
}

View File

@@ -0,0 +1,149 @@
/**
* 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.sword2;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import org.dspace.app.rest.test.AbstractWebClientIntegrationTest;
import org.dspace.services.ConfigurationService;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
/**
* Integration test to verify the /swordv2 endpoint is responding as a valid SWORDv2 endpoint.
* This tests that our dspace-swordv2 module is running at this endpoint.
* <P>
* This is a AbstractWebClientIntegrationTest because testing dspace-swordv2 requires
* running a web server (as dspace-swordv2 makes use of Servlets, not Controllers).
*
* @author Tim Donohue
*/
// Ensure the SWORDv2 SERVER IS ENABLED before any tests run.
// This annotation overrides default DSpace config settings loaded into Spring Context
@TestPropertySource(properties = {"swordv2-server.enabled = true"})
public class Swordv2IT extends AbstractWebClientIntegrationTest {
@Autowired
private ConfigurationService configurationService;
// All SWORD v2 paths that we test against
private final String SERVICE_DOC_PATH = "/swordv2/servicedocument";
private final String COLLECTION_PATH = "/swordv2/collection";
private final String MEDIA_RESOURCE_PATH = "/swordv2/edit-media";
private final String CONTAINER_PATH = "/swordv2/edit";
private final String STATEMENT_PATH = "/swordv2/statement";
@Before
public void onlyRunIfConfigExists() {
// These integration tests REQUIRE that SWORDv2WebConfig is found/available (as this class deploys SWORDv2)
// If this class is not available, the below "Assume" will cause all tests to be SKIPPED
// NOTE: SWORDv2WebConfig is provided by the 'dspace-swordv2' module
try {
Class.forName("org.dspace.app.configuration.SWORDv2WebConfig");
} catch (ClassNotFoundException ce) {
Assume.assumeNoException(ce);
}
// Ensure SWORDv2 URL configurations are set correctly (based on our integration test server's paths)
// SWORDv2 validates requests against these configs, and throws a 404 if they don't match the request path
configurationService.setProperty("swordv2-server.servicedocument.url", getURL(SERVICE_DOC_PATH));
}
@Test
public void serviceDocumentUnauthorizedTest() throws Exception {
// Attempt to GET the ServiceDocument without first authenticating
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
public void serviceDocumentTest() throws Exception {
// Attempt to GET the ServiceDocument as an Admin user.
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH,
admin.getEmail(), password);
// Expect a 200 response code, and an ATOM UTF-8 document
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
assertThat(response.getHeaders().getContentType().toString(),
equalTo("application/atomserv+xml;charset=UTF-8"));
// Check for correct SWORD version in response body
assertThat(response.getBody(),
containsString("<version xmlns=\"http://purl.org/net/sword/terms/\">2.0</version>"));
}
@Test
public void collectionUnauthorizedTest() throws Exception {
// Attempt to POST to /collection endpoint without sending authentication information
ResponseEntity<String> response = postResponseAsString(COLLECTION_PATH, null, null, null);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void collectionTest() throws Exception {
// TODO: Actually test collection endpoint via SWORDv2.
// Currently, we are just ensuring the /collection endpoint exists (see above) and isn't throwing a 404
}
@Test
public void mediaResourceUnauthorizedTest() throws Exception {
// Attempt to POST to /mediaresource endpoint without sending authentication information
ResponseEntity<String> response = postResponseAsString(MEDIA_RESOURCE_PATH, null, null, null);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void mediaResourceTest() throws Exception {
// TODO: Actually test this endpoint via SWORDv2.
// Currently, we are just ensuring the /mediaresource endpoint exists (see above) and isn't throwing a 404
}
@Test
public void containerUnauthorizedTest() throws Exception {
// Attempt to POST to /container endpoint without sending authentication information
ResponseEntity<String> response = postResponseAsString(CONTAINER_PATH, null, null, null);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void containerTest() throws Exception {
// TODO: Actually test this endpoint via SWORDv2.
// Currently, we are just ensuring the /container endpoint exists (see above) and isn't throwing a 404
}
@Test
public void statementUnauthorizedTest() throws Exception {
// Attempt to GET /statement endpoint without sending authentication information
ResponseEntity<String> response = getResponseAsString(STATEMENT_PATH);
// Expect a 401 response code
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
}
@Test
@Ignore
public void statementTest() throws Exception {
// TODO: Actually test this endpoint via SWORDv2.
// Currently, we are just ensuring the /statement endpoint exists (see above) and isn't throwing a 404
}
}

View File

@@ -2,10 +2,10 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<name>DSpace SWORD</name>
<description>
DSpace SWORD Deposit Service Provider Web Application
DSpace SWORD Deposit Service Provider Extension
</description>
<!--
@@ -24,28 +24,6 @@
<root.basedir>${basedir}/..</root.basedir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
<!-- In version 2.1-alpha-1, this was incorrectly named warSourceExcludes -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<warSourceExcludes>WEB-INF/lib/*.jar</warSourceExcludes>
<!-- Filter the web.xml (needed for IDE compatibility/debugging) -->
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>oracle-support</id>
@@ -95,6 +73,19 @@
<artifactId>dspace-api-lang</artifactId>
</dependency>
<!-- Needed to support Spring @Configuration classes (to register servlets/beans with Spring Boot webapp) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>

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.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* SWORD webapp configuration. Replaces the old web.xml
* <p>
* This @Configuration class is automatically discovered by Spring via a @ComponentScan.
* <p>
* All SWORD web configurations (beans) can be enabled or disabled by setting "sword-server.enabled"
* to true or false, respectively (in your DSpace configuration). Default is "false".
* <p>
* All @Value annotated configurations below can also be overridden in your DSpace configuration.
*
* @author Tim Donohue
*/
@Configuration
public class SWORDWebConfig {
// Path where SWORD should be deployed (when enabled). Defaults to "sword"
@Value("${sword-server.path:sword}")
private String swordPath;
// SWORD Server class. Defaults to "org.dspace.sword.DSpaceSWORDServer"
@Value("${sword-server.class:org.dspace.sword.DSpaceSWORDServer}")
private String serverClass;
// SWORD Authentication Method. Defaults to "Basic"
@Value("${sword-server.authentication-method:Basic}")
private String authenticationMethod;
/**
* Initialize all required Context Parameters (i.e. <context-param> in web.xml), based on configurations above.
* <p>
* This bean is only loaded when sword-server.enabled = true
* @return ServletContextInitializer which includes all required params
*/
@Bean
@ConditionalOnProperty("sword-server.enabled")
public ServletContextInitializer swordv1ContextInitializer() {
return servletContext -> {
servletContext.setInitParameter("sword-server-class", serverClass);
servletContext.setInitParameter("authentication-method", authenticationMethod);
};
}
// Servlet Beans. All of the below bean definitions map servlets to respond to specific URL patterns
// These are the combined equivalent of <servlet> and <servlet-mapping> in web.xml
// All beans are only loaded when sword-server.enabled = true
@Bean
@ConditionalOnProperty("sword-server.enabled")
public ServletRegistrationBean swordv1ServiceDocumentBean() {
ServletRegistrationBean bean = new ServletRegistrationBean( new org.purl.sword.server.ServiceDocumentServlet(),
"/" + swordPath + "/servicedocument/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("sword-server.enabled")
public ServletRegistrationBean swordv1DepositBean() {
ServletRegistrationBean bean = new ServletRegistrationBean( new org.purl.sword.server.DepositServlet(),
"/" + swordPath + "/deposit/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("sword-server.enabled")
public ServletRegistrationBean swordv1MediaLinkBean() {
ServletRegistrationBean bean = new ServletRegistrationBean( new org.purl.sword.server.AtomDocumentServlet(),
"/" + swordPath + "/media-link/*");
bean.setLoadOnStartup(1);
return bean;
}
}

View File

@@ -27,11 +27,6 @@ import org.purl.sword.base.SWORDException;
*/
public class AtomDocumentServlet extends DepositServlet {
public AtomDocumentServlet()
throws ServletException {
super();
}
/**
* Process the get request.
*/

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
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/
-->
<web-app id="DSpace-SWORDv1" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>DSpace SWORD Server</display-name>
<!-- ConfigurationService initialization for dspace.dir -->
<context-param>
<description>The location of the DSpace home directory</description>
<param-name>dspace.dir</param-name>
<param-value>${dspace.dir}</param-value>
</context-param>
<context-param>
<description>The SWORDServer class name</description>
<param-name>sword-server-class</param-name>
<param-value>org.dspace.sword.DSpaceSWORDServer</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>${dspace.dir}/config/log4j2.xml</param-value>
<description>
The location of the Log4J configuration
</description>
</context-param>
<context-param>
<description>The type of authentication used : [Basic|None]</description>
<param-name>authentication-method</param-name>
<param-value>Basic</param-value>
</context-param>
<!--
Listener to initialise DSpace configuration and clean up the application
-->
<listener>
<listener-class>
org.dspace.app.util.DSpaceContextListener
</listener-class>
</listener>
<listener>
<listener-class>
org.dspace.servicemanager.servlet.DSpaceKernelServletContextListener
</listener-class>
</listener>
<!-- Servlets -->
<servlet>
<servlet-name>servicedocument</servlet-name>
<servlet-class>org.purl.sword.server.ServiceDocumentServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>deposit</servlet-name>
<servlet-class>org.purl.sword.server.DepositServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>media-link</servlet-name>
<servlet-class>org.purl.sword.server.AtomDocumentServlet</servlet-class>
</servlet>
<!-- Servlet Mappings -->
<servlet-mapping>
<servlet-name>servicedocument</servlet-name>
<url-pattern>/servicedocument/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>deposit</servlet-name>
<url-pattern>/deposit/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>media-link</servlet-name>
<url-pattern>/media-link/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -2,9 +2,9 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<name>DSpace SWORD v2</name>
<description>DSpace SWORD v2 Deposit Service Provider Web Application</description>
<description>DSpace SWORD v2 Deposit Service Provider Extension</description>
<!--
A Parent POM that Maven inherits DSpace Default
@@ -22,29 +22,6 @@
<root.basedir>${basedir}/..</root.basedir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
<!-- In version 2.1-alpha-1, this was incorrectly named warSourceExcludes -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<warSourceExcludes>WEB-INF/lib/*.jar</warSourceExcludes>
<!-- Filter the web.xml (needed for IDE compatibility/debugging) -->
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>oracle-support</id>
@@ -83,6 +60,8 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- SWORDv2 Java implementation -->
<dependency>
<groupId>org.swordapp</groupId>
<artifactId>sword2-server</artifactId>
@@ -98,26 +77,41 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<!-- Exclude an old version of Jena in favor of new Apache Jena, which is pulled in by dspace-parent -->
<exclusion>
<groupId>com.hp.hpl.jena</groupId>
<artifactId>jena</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.swordapp</groupId>
<artifactId>sword2-server</artifactId>
<version>1.0</version>
<type>war</type>
</dependency>
<!-- DSpace Java API -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
</dependency>
<!-- Needed to support Spring @Configuration classes (to register servlets/beans with Spring Boot webapp) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
@@ -134,7 +128,6 @@
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-client</artifactId>
<version>1.1.3</version>
<exclusions>
<exclusion>
<groupId>org.apache.ws.commons.axiom</groupId>

View File

@@ -0,0 +1,143 @@
/**
* 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.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* SWORDv2 webapp configuration. Replaces the old web.xml
* <p>
* This @Configuration class is automatically discovered by Spring via a @ComponentScan.
* <p>
* All SWORDv2 web configurations (beans) can be enabled or disabled by setting "swordv2-server.enabled"
* to true or false, respectively (in your DSpace configuration). Default is "false".
* <p>
* All @Value annotated configurations below can also be overridden in your DSpace configuration.
*
* @author Tim Donohue
*/
@Configuration
public class SWORDv2WebConfig {
// Path where SWORDv2 should be deployed (when enabled). Defaults to "swordv2"
@Value("${swordv2-server.path:swordv2}")
private String swordv2Path;
// ServiceDocumentManager server implementation class name (default: org.dspace.sword2.ServiceDocumentManagerDSpace)
@Value("${swordv2-server.service-document-impl:org.dspace.sword2.ServiceDocumentManagerDSpace}")
private String serviceDocImpl;
// CollectionListManager server implementation class name (default: org.dspace.sword2.CollectionListManagerDSpace)
@Value("${swordv2-server.collection-list-impl:org.dspace.sword2.CollectionListManagerDSpace}")
private String collectionListImpl;
// CollectionDepositManager server implementation class name
// (default: org.dspace.sword2.CollectionDepositManagerDSpace)
@Value("${swordv2-server.collection-deposit-impl:org.dspace.sword2.CollectionDepositManagerDSpace}")
private String collectionDepositImpl;
// MediaResourceManager server implementation class name (default: org.dspace.sword2.MediaResourceManagerDSpace)
@Value("${swordv2-server.media-resource-impl:org.dspace.sword2.MediaResourceManagerDSpace}")
private String mediaResourceImpl;
// ContainerManager server implementation class name (default: org.dspace.sword2.ContainerManagerDSpace)
@Value("${swordv2-server.container-impl:org.dspace.sword2.ContainerManagerDSpace}")
private String containerImpl;
// StatementManager server implementation class name (default: org.dspace.sword2.StatementManagerDSpace)
@Value("${swordv2-server.statement-impl:org.dspace.sword2.StatementManagerDSpace}")
private String statementImpl;
// SwordConfiguration server implementation class name (default: org.dspace.sword2.SwordConfigurationDSpace)
@Value("${swordv2-server.config-impl:org.dspace.sword2.SwordConfigurationDSpace}")
private String configImpl;
// Authentication Method. Defaults to "Basic"
@Value("${swordv2-server.auth-type:Basic}")
private String authenticationMethod;
/**
* Initialize all required Context Parameters (i.e. <context-param> in web.xml), based on configurations above.
* <p>
* This bean is only loaded when swordv2-server.enabled = true
* @return ServletContextInitializer which includes all required params
*/
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletContextInitializer swordv2ContextInitializer() {
return servletContext -> {
servletContext.setInitParameter("service-document-impl", serviceDocImpl);
servletContext.setInitParameter("collection-list-impl", collectionListImpl);
servletContext.setInitParameter("collection-deposit-impl", collectionDepositImpl);
servletContext.setInitParameter("media-resource-impl", mediaResourceImpl);
servletContext.setInitParameter("container-impl", containerImpl);
servletContext.setInitParameter("statement-impl", statementImpl);
servletContext.setInitParameter("config-impl", configImpl);
servletContext.setInitParameter("authentication-method", authenticationMethod);
};
}
// Servlet Beans. All of the below bean definitions map servlets to respond to specific URL patterns
// These are the combined equivalent of <servlet> and <servlet-mapping> in web.xml
// All beans are only loaded when swordv2-server.enabled = true
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletRegistrationBean swordv2ServiceDocumentBean() {
ServletRegistrationBean bean =
new ServletRegistrationBean(new org.swordapp.server.servlets.ServiceDocumentServletDefault(),
"/" + swordv2Path + "/servicedocument/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletRegistrationBean swordv2CollectionBean() {
ServletRegistrationBean bean =
new ServletRegistrationBean( new org.swordapp.server.servlets.CollectionServletDefault(),
"/" + swordv2Path + "/collection/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletRegistrationBean swordv2MediaResourceBean() {
ServletRegistrationBean bean =
new ServletRegistrationBean( new org.swordapp.server.servlets.MediaResourceServletDefault(),
"/" + swordv2Path + "/edit-media/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletRegistrationBean swordv2ContainerBean() {
ServletRegistrationBean bean =
new ServletRegistrationBean( new org.swordapp.server.servlets.ContainerServletDefault(),
"/" + swordv2Path + "/edit/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
@ConditionalOnProperty("swordv2-server.enabled")
public ServletRegistrationBean swordv2StatementBean() {
ServletRegistrationBean bean =
new ServletRegistrationBean( new org.swordapp.server.servlets.StatementServletDefault(),
"/" + swordv2Path + "/statement/*");
bean.setLoadOnStartup(1);
return bean;
}
}

View File

@@ -1,154 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
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/
-->
<web-app id="DSpace-SWORDv2" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>DSpace SWORD 2.0 Server</display-name>
<!-- ConfigurationService initialization for dspace.dir -->
<context-param>
<description>The location of the DSpace home directory</description>
<param-name>dspace.dir</param-name>
<param-value>${dspace.dir}</param-value>
</context-param>
<!-- Configuration Information -->
<context-param>
<description>The ServiceDocumentManager server implementation class name</description>
<param-name>service-document-impl</param-name>
<param-value>org.dspace.sword2.ServiceDocumentManagerDSpace</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>${dspace.dir}/config/log4j2.xml</param-value>
<description>
The location of the Log4J configuration
</description>
</context-param>
<!-- This can be omitted if the server does not wish to support
listing collection contents -->
<context-param>
<description>The CollectionListManager server implementation class name</description>
<param-name>collection-list-impl</param-name>
<param-value>org.dspace.sword2.CollectionListManagerDSpace</param-value>
</context-param>
<context-param>
<description>The CollectionDepositManager server implementation class name</description>
<param-name>collection-deposit-impl</param-name>
<param-value>org.dspace.sword2.CollectionDepositManagerDSpace</param-value>
</context-param>
<context-param>
<description>The MediaResourceManager server implementation class name</description>
<param-name>media-resource-impl</param-name>
<param-value>org.dspace.sword2.MediaResourceManagerDSpace</param-value>
</context-param>
<context-param>
<description>The ContainerManager server implementation class name</description>
<param-name>container-impl</param-name>
<param-value>org.dspace.sword2.ContainerManagerDSpace</param-value>
</context-param>
<context-param>
<description>The StatementManager server implementation class name</description>
<param-name>statement-impl</param-name>
<param-value>org.dspace.sword2.StatementManagerDSpace</param-value>
</context-param>
<!-- This option here is an actual implementation of the configuration class, which
contains some default values -->
<context-param>
<description>The SwordConfiguration server implementation class name</description>
<param-name>config-impl</param-name>
<param-value>org.dspace.sword2.SwordConfigurationDSpace</param-value>
</context-param>
<context-param>
<description>The type of authentication used : [Basic|None]</description>
<param-name>authentication-method</param-name>
<param-value>Basic</param-value>
</context-param>
<!--
Listener to initialise DSpace configuration and clean up the application
-->
<listener>
<listener-class>
org.dspace.app.util.DSpaceContextListener
</listener-class>
</listener>
<listener>
<listener-class>
org.dspace.servicemanager.servlet.DSpaceKernelServletContextListener
</listener-class>
</listener>
<!-- Servlets -->
<servlet>
<servlet-name>servicedocument</servlet-name>
<servlet-class>org.swordapp.server.servlets.ServiceDocumentServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>collection</servlet-name>
<servlet-class>org.swordapp.server.servlets.CollectionServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>mediaresource</servlet-name>
<servlet-class>org.swordapp.server.servlets.MediaResourceServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>container</servlet-name>
<servlet-class>org.swordapp.server.servlets.ContainerServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>statement</servlet-name>
<servlet-class>org.swordapp.server.servlets.StatementServletDefault</servlet-class>
</servlet>
<!-- Servlet Mappings -->
<servlet-mapping>
<servlet-name>servicedocument</servlet-name>
<url-pattern>/servicedocument/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>collection</servlet-name>
<url-pattern>/collection/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>mediaresource</servlet-name>
<url-pattern>/edit-media/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>container</servlet-name>
<url-pattern>/edit/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>statement</servlet-name>
<url-pattern>/statement/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -26,7 +26,7 @@ dspace.dir = /dspace
dspace.hostname = localhost
# DSpace base host URL. Include port number etc.
dspace.baseUrl = http://localhost:8080
dspace.baseUrl = http://localhost:8080/spring-rest
# Full link your end users will use to access DSpace. In most cases, this will be the baseurl followed by
# the context path to the UI you are using.
@@ -37,7 +37,7 @@ dspace.url = ${dspace.baseUrl}
# This is the URL that will be used for the REST endpoints to be served on.
# This will typically be followed by /api to determine the root endpoints.
dspace.restUrl = ${dspace.baseUrl}/spring-rest
dspace.restUrl = ${dspace.baseUrl}
# Optional: DSpace URL for mobile access
# This
@@ -766,6 +766,24 @@ org.dspace.app.batchitemimport.work.dir = ${dspace.dir}/imports
# XSL file configured by: crosswalk.dissemination.DataCite.stylesheet file.)
#identifier.doi.ezid.publisher = a publisher
##### Registry Loader #####
# Metadata namespaces. These files are loaded from
# ${dspace.dir}/config/registries/ into the database during installation
# and upgrade.
registry.metadata.load = dublin-core-types.xml
registry.metadata.load = dcterms-types.xml
registry.metadata.load = local-types.xml
registry.metadata.load = eperson-types.xml
registry.metadata.load = sword-metadata.xml
registry.metadata.load = relationship-formats.xml
registry.metadata.load = person-types.xml
registry.metadata.load = project-types.xml
registry.metadata.load = orgunit-types.xml
registry.metadata.load = journal-types.xml
registry.metadata.load = journalissue-types.xml
registry.metadata.load = journalvolume-types.xml
#---------------------------------------------------------------#
#--------------JSPUI & XMLUI CONFIGURATIONS---------------------#

View File

@@ -4,12 +4,24 @@
# These configs are used by the OAI-PMH interface #
#---------------------------------------------------------------#
# Whether or not to enable the OAI module
# When "true", the OAI module is accessible on ${oai.path}
# When "false" or commented out, OAI is disabled/inaccessible.
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#oai.enabled = true
# Path where OAI module is available
# Defaults to "oai", which means the OAI module would be available
# at ${dspace.restURL}/oai/
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
oai.path = oai
# Storage: solr | database (solr is recommended)
oai.storage=solr
# The base URL of the OAI webapp (do not include the context e.g. /request, /openaire, etc).
# Note: Comment out if you want to fallback to the request's URL.
oai.url = ${dspace.baseUrl}/oai
oai.url = ${dspace.baseUrl}/${oai.path}
# Base solr index
oai.solr.url=${solr.server}/oai

View File

@@ -1,12 +1,29 @@
# These configs are used by dspace-rdf and the buildin Linked Data export (rdfizer)
#---------------------------------------------------------------#
#----------------RDF (LINKED DATA) CONFIGURATIONS---------------#
#---------------------------------------------------------------#
# These configs are only used by the RDF (Linked Data) #
# interface (provided by dspace-rdf and the built-in Linked #
# Data export (rdfizer)).
#---------------------------------------------------------------#
# Whether or not to enable the RDF module
# When "true", the RDF module is accessible on ${rdf.path}
# When "false" or commented out, RDF is disabled/inaccessible.
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#rdf.enabled = true
# Path where RDF module is available (in the Spring REST webapp)
# Defaults to "rdf", which means the RDF module would be available
# at ${dspace.restURL}/rdf/
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
rdf.path = rdf
# Configure if content negotiation should be enabled
rdf.contentNegotiation.enable = false
# Set the url of the dspace-rdf module here. This is necessary to use content
# Set the url of the RDF module here. This is necessary to use content
# negotiation
rdf.contextPath = ${dspace.baseUrl}/rdf
rdf.contextPath = ${dspace.baseUrl}/${rdf.path}
# Address of the public SPARQL endpoint

View File

@@ -1,9 +1,17 @@
#---------------------------------------------------------------#
#--------------------REST CONFIGURATIONS------------------------#
#---------------------------------------------------------------#
# These configs are used by the REST module #
# These configs are used by the RESTv7 module #
#---------------------------------------------------------------#
# Allowed CORS origins. Defaults to * (everywhere)
# Multiple allowed origin URLs may be comma separated
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
rest.cors.allowed-origins = *
#---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module #
#---------------------------------------------------------------#
# record stats in DSpace statistics module
rest.stats = true
@@ -137,4 +145,4 @@ rest.report-regex-xml-entity = ^.*&#.*$
rest.report-regex-non-ascii = ^.*[^\\p{ASCII}].*$
# The maximum number of results to return for 1 request
rest.search.max.results = 100
rest.search.max.results = 100

View File

@@ -6,6 +6,28 @@
# SWORD protocol) #
#---------------------------------------------------------------#
# Whether or not to enable the SWORD v1 module
# When "true", the SWORD module is accessible on ${sword-server.path}
# When "false" or commented out, SWORD is disabled/inaccessible.
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#sword-server.enabled = true
# Path where SWORD v1 module is available (in the Spring REST webapp)
# Defaults to "sword", which means the SWORD mould would be available
# at ${dspace.restURL}/sword/
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#sword-server.path = sword
# The SWORDServer class name (in charge of all SWORD activities)
# This Java class must implement 'org.purl.sword.server.SWORDServer'
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#sword-server.class = org.dspace.sword.DSpaceSWORDServer
# The Authentication Method SWORD should use.
# Valid values are "Basic" or "None". Default is "Basic"
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#sword-server.authentication-method = Basic
# tell the SWORD METS implementation which package ingester to use
# to install deposited content. This should refer to one of the
# classes configured for:

View File

@@ -6,6 +6,18 @@
# SWORD 2.0 protocol) #
#---------------------------------------------------------------#
# Whether or not to enable the SWORD v2 module
# When "true", the SWORD v2 module is accessible on ${swordv2-server.path}
# When "false" or commented out, SWORD v2 is disabled/inaccessible.
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#swordv2-server.enabled = true
# Path where SWORD v2 module is available (in the Spring REST webapp)
# Defaults to "swordv2", which means the SWORD v2 module would be available
# at ${dspace.restURL}/swordv2/
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
#swordv2-server.path = swordv2
# the base url of the sword 2.0 system
#
# the default if {dspace.url}/swordv2
@@ -194,6 +206,32 @@ swordv2-server.generator.version = 2.0
# Other valid values: 'None'
swordv2-server.auth-type = Basic
# SWORD v2 server implementation classes
# These are the default implementation classes to use for SWORD functions.
# Uncomment if you wish to change the implementation class.
# (Requires reboot of servlet container, e.g. Tomcat, to reload these settings)
#
# ServiceDocumentManager server implementation
#swordv2-server.service-document-impl = org.dspace.sword2.ServiceDocumentManagerDSpace
#
# CollectionListManager server implementation class
#swordv2-server.collection-list-impl = org.dspace.sword2.CollectionListManagerDSpace
#
# CollectionDepositManager server implementation class
#swordv2-server.collection-deposit-impl = org.dspace.sword2.CollectionDepositManagerDSpace
#
# MediaResourceManager server implementation class
#swordv2-server.media-resource-impl = org.dspace.sword2.MediaResourceManagerDSpace
#
# ContainerManager server implementation class name
#swordv2-server.container-impl = org.dspace.sword2.ContainerManagerDSpace
#
# StatementManager server implementation class
#swordv2-server.statement-impl = org.dspace.sword2.StatementManagerDSpace
#
# SwordConfiguration server implementation class
#swordv2-server.config-impl = org.dspace.sword2.SwordConfigurationDSpace
# The location where uploaded files and packages are
# stored while being processed
swordv2-server.upload.tempdir = ${upload.temp.dir}

View File

@@ -1,169 +0,0 @@
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace.modules</groupId>
<artifactId>oai</artifactId>
<packaging>war</packaging>
<name>DSpace OAI-PMH :: Local Customizations</name>
<description>
This project allows you to overlay your own local OAI customizations
on top of the default OAI-PMH web application provided with DSpace.
</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>
<exclusions>
<exclusion>
<groupId>com.lyncode</groupId>
<artifactId>builder-commons</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
<type>jar</type>
<classifier>classes</classifier>
<exclusions>
<exclusion>
<groupId>com.lyncode</groupId>
<artifactId>builder-commons</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!--
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
-->
<dependency>
<groupId>com.lyncode</groupId>
<artifactId>builder-commons</artifactId>
<version>1.0.2</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Note: XOAI requires hamcrest both for testing and runtime -->
<!-- As our Parent POM sets this to 'test' scope, we must override it -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<developers>
<developer>
<id>lyncode</id>
<email>dspace@lyncode.com</email>
<name>DSpace @ Lyncode</name>
<url>http://www.lyncode.com</url>
</developer>
</developers>
</project>

View File

@@ -1,112 +0,0 @@
<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>rdf</artifactId>
<packaging>war</packaging>
<name>DSpace RDF :: Local Customizations</name>
<description>
Overlay RDF customizations
</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-rdf</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,129 +0,0 @@
<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>sword</artifactId>
<packaging>war</packaging>
<name>DSpace SWORD :: Local Customizations</name>
<description>
This project allows you to overlay your own local SWORD customizations
on top of the default SWORD web application provided with DSpace.
</description>
<!--
A Parent POM that Maven inherits DSpace Default
POM attributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>modules</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>
</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>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,144 +0,0 @@
<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>swordv2</artifactId>
<packaging>war</packaging>
<name>DSpace SWORD v2 :: Local Customizations</name>
<description>
This project allows you to overlay your own local SWORD v2 customizations
on top of the default SWORD v2 web application provided with DSpace.
</description>
<!--
A Parent POM that Maven inherits DSpace Default
POM attributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>modules</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>
</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>
<profile>
<id>postgres-support</id>
<activation>
<property>
<name>!db.name</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.dspace.modules</groupId>
<artifactId>additions</artifactId>
</dependency>
<!-- DSpace Custom SWORDv2 Web application -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<type>war</type>
</dependency>
<!-- DSpace Implementation of SWORDv2 Provider -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -198,13 +198,11 @@
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
<classifier>classes</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-rdf</artifactId>
<type>war</type>
<scope>compile</scope>
</dependency>
<dependency>
@@ -227,13 +225,11 @@
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
<classifier>classes</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<classifier>classes</classifier>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -290,8 +286,6 @@
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
<type>jar</type>
<classifier>classes</classifier>
<exclusions>
<exclusion>
<groupId>com.lyncode</groupId>

View File

@@ -1,219 +0,0 @@
<?xml version="1.0" encoding="UTF-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/
-->
<!--
THIS FILE EXISTS TO SIMPLIFY TESTING IN DOCKER.
This file overrides the solr localhost restriction.
-->
<web-app version='2.5'
xmlns='http://java.sun.com/xml/ns/javaee'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd'>
<!-- Uncomment if you are trying to use a Resin version before 3.0.19.
Their XML implementation isn't entirely compatible with Xerces.
Below are the implementations to use with Sun's JVM.
<system-property javax.xml.xpath.XPathFactory=
"com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl"/>
<system-property javax.xml.parsers.DocumentBuilderFactory=
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"/>
<system-property javax.xml.parsers.SAXParserFactory=
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"/>
-->
<env-entry>
<description>Solr home: configuration, cores etc.</description>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>${dspace.dir}/solr</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
<!-- Tell Solr where its log4j configuration is located -->
<!-- NOTE: Solr cannot use the default DSpace log4j configuration as it
isn't initialized until the DSpace Kernel starts up, and we don't want
Solr to depend on the DSpace Kernel/API -->
<context-param>
<description>
URL locating a Log4J configuration file (properties or XML).
</description>
<param-name>log4jConfiguration</param-name>
<param-value>${dspace.dir}/config/log4j-solr.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<description>Activate logging</description>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
<filter>
<filter-name>LocalHostRestrictionFilter</filter-name>
<filter-class>org.dspace.solr.filters.LocalHostRestrictionFilter</filter-class>
</filter>
<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
<filter>
<filter-name>SolrRequestFilter</filter-name>
<filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>
<!-- If you are wiring Solr into a larger web application which controls
the web context root, you will probably want to mount Solr under
a path prefix (app.war with /app/solr mounted into it, for example).
You will need to put this prefix in front of the SolrDispatchFilter
url-pattern mapping too (/solr/*), and also on any paths for
legacy Solr servlet mappings you may be using.
For the Admin UI to work properly in a path-prefixed configuration,
the admin folder containing the resources needs to be under the app context root
named to match the path-prefix. For example:
.war
xxx
js
main.js
-->
<!--
<init-param>
<param-name>path-prefix</param-name>
<param-value>/xxx</param-value>
</init-param>
-->
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<!--
<filter-mapping>
<filter-name>LocalHostRestrictionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<filter-mapping>
<!--
NOTE: When using multicore, /admin JSP URLs with a core specified
such as /solr/coreName/admin/stats.jsp get forwarded by a
RequestDispatcher to /solr/admin/stats.jsp with the specified core
put into request scope keyed as "org.apache.solr.SolrCore".
It is unnecessary, and potentially problematic, to have the SolrDispatchFilter
configured to also filter on forwards. Do not configure
this dispatcher as <dispatcher>FORWARD</dispatcher>.
-->
<filter-name>SolrRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Otherwise it will continue to the old servlets -->
<servlet>
<servlet-name>Zookeeper</servlet-name>
<servlet-class>org.apache.solr.servlet.ZookeeperInfoServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>LoadAdminUI</servlet-name>
<servlet-class>org.apache.solr.servlet.LoadAdminUiServlet</servlet-class>
</servlet>
<!-- Remove in Solr 5.0 -->
<!-- This sends SC_MOVED_PERMANENTLY (301) for resources that changed in 4.0 -->
<servlet>
<servlet-name>RedirectOldAdminUI</servlet-name>
<servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class>
<init-param>
<param-name>destination</param-name>
<param-value>${context}/#/</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RedirectOldZookeeper</servlet-name>
<servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class>
<init-param>
<param-name>destination</param-name>
<param-value>${context}/zookeeper</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RedirectLogging</servlet-name>
<servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class>
<init-param>
<param-name>destination</param-name>
<param-value>${context}/#/~logging</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>SolrRestApi</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.application</param-name>
<param-value>org.apache.solr.rest.SolrRestApi</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RedirectOldAdminUI</servlet-name>
<url-pattern>/admin/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectOldAdminUI</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectOldZookeeper</servlet-name>
<url-pattern>/zookeeper.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectLogging</servlet-name>
<url-pattern>/logging</url-pattern>
</servlet-mapping>
<!-- Servlet Mapping -->
<servlet-mapping>
<servlet-name>Zookeeper</servlet-name>
<url-pattern>/zookeeper</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LoadAdminUI</servlet-name>
<url-pattern>/admin.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SolrRestApi</servlet-name>
<url-pattern>/schema/*</url-pattern>
</servlet-mapping>
<mime-mapping>
<extension>.xsl</extension>
<!-- per http://www.w3.org/TR/2006/PR-xslt20-20061121/ -->
<mime-type>application/xslt+xml</mime-type>
</mime-mapping>
<welcome-file-list>
<welcome-file>admin.html</welcome-file>
</welcome-file-list>
</web-app>

Some files were not shown because too many files have changed in this diff Show More