initial commit
This commit is contained in:
commit
3d79ba0650
111 changed files with 6133 additions and 0 deletions
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
/data
|
||||
/work
|
||||
/logs
|
||||
/.idea
|
||||
/target
|
||||
.DS_Store
|
||||
*.iml
|
||||
/.settings
|
||||
/.classpath
|
||||
/.project
|
||||
/.gradle
|
||||
build
|
||||
/plugins
|
||||
/sessions
|
||||
*~
|
||||
*.MARC
|
71
build.gradle
Normal file
71
build.gradle
Normal file
|
@ -0,0 +1,71 @@
|
|||
plugins {
|
||||
id "org.sonarqube" version "2.2"
|
||||
id "org.ajoberstar.github-pages" version "1.6.0-rc.1"
|
||||
id "org.xbib.gradle.plugin.jbake" version "1.1.0"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
||||
group = 'org.xbib'
|
||||
version = '1.0.0'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'signing'
|
||||
apply plugin: 'findbugs'
|
||||
apply plugin: 'pmd'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: "jacoco"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
wagon
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.apache.logging.log4j:log4j-core:2.7'
|
||||
testCompile 'org.apache.logging.log4j:log4j-jul:2.7'
|
||||
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:all" << "-profile" << "compact2"
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
showStandardStreams = false
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier 'javadoc'
|
||||
}
|
||||
artifacts {
|
||||
archives sourcesJar, javadocJar
|
||||
}
|
||||
if (project.hasProperty('signing.keyId')) {
|
||||
signing {
|
||||
sign configurations.archives
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "${rootProject.projectDir}/gradle/ext.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/publish.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/sonarqube.gradle"
|
||||
|
||||
}
|
12
gradle/ext.gradle
Normal file
12
gradle/ext.gradle
Normal file
|
@ -0,0 +1,12 @@
|
|||
ext {
|
||||
user = 'xbib'
|
||||
projectName = 'oai'
|
||||
projectDescription = 'Open Archive Initiative library for Java'
|
||||
scmUrl = 'https://github.com/xbib/oai'
|
||||
scmConnection = 'scm:git:git://github.com/xbib/oai.git'
|
||||
scmDeveloperConnection = 'scm:git:git://github.com/xbib/oai.git'
|
||||
versions = [
|
||||
'tcnative': '1.1.33.Fork23',
|
||||
'alpnboot': '8.1.9.v20160720'
|
||||
]
|
||||
}
|
66
gradle/publish.gradle
Normal file
66
gradle/publish.gradle
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
task xbibUpload(type: Upload, dependsOn: build) {
|
||||
configuration = configurations.archives
|
||||
uploadDescriptor = true
|
||||
repositories {
|
||||
if (project.hasProperty('xbibUsername')) {
|
||||
mavenDeployer {
|
||||
configuration = configurations.wagon
|
||||
repository(url: uri('scpexe://xbib.org/repository')) {
|
||||
authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task sonatypeUpload(type: Upload, dependsOn: build) {
|
||||
configuration = configurations.archives
|
||||
uploadDescriptor = true
|
||||
repositories {
|
||||
if (project.hasProperty('ossrhUsername')) {
|
||||
mavenDeployer {
|
||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||
repository(url: uri(ossrhReleaseUrl)) {
|
||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||
}
|
||||
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
|
||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||
}
|
||||
pom.project {
|
||||
groupId project.group
|
||||
artifactId project.name
|
||||
version project.version
|
||||
name project.name
|
||||
description projectDescription
|
||||
packaging 'jar'
|
||||
inceptionYear '2016'
|
||||
url scmUrl
|
||||
organization {
|
||||
name 'xbib'
|
||||
url 'http://xbib.org'
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id user
|
||||
name 'Jörg Prante'
|
||||
email 'joergprante@gmail.com'
|
||||
url 'https://github.com/jprante'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url scmUrl
|
||||
connection scmConnection
|
||||
developerConnection scmDeveloperConnection
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name 'The Apache License, Version 2.0'
|
||||
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
gradle/sonarqube.gradle
Normal file
41
gradle/sonarqube.gradle
Normal file
|
@ -0,0 +1,41 @@
|
|||
tasks.withType(FindBugs) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = false
|
||||
}
|
||||
}
|
||||
tasks.withType(Pmd) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
tasks.withType(Checkstyle) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled true
|
||||
csv.enabled false
|
||||
xml.destination "${buildDir}/reports/jacoco-xml"
|
||||
html.destination "${buildDir}/reports/jacoco-html"
|
||||
}
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.projectName", "${project.group} ${project.name}"
|
||||
property "sonar.sourceEncoding", "UTF-8"
|
||||
property "sonar.tests", "src/test/java"
|
||||
property "sonar.scm.provider", "git"
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
property "sonar.junit.reportsPath", "build/test-results/test/"
|
||||
}
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#Mon Oct 03 00:03:03 CEST 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
|
33
oai-client/build.gradle
Normal file
33
oai-client/build.gradle
Normal file
|
@ -0,0 +1,33 @@
|
|||
configurations {
|
||||
alpnboot
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':oai-common')
|
||||
compile "org.xbib.helianthus:helianthus-client:1.0.3"
|
||||
if ('os x' == org.gradle.internal.os.OperatingSystem.current().getFamilyName()) {
|
||||
testCompile "io.netty:netty-tcnative:${versions.tcnative}:osx-x86_64"
|
||||
}
|
||||
if ('linux' == org.gradle.internal.os.OperatingSystem.current().getFamilyName()) {
|
||||
if (new File("/etc/redhat-release").exists()) {
|
||||
// use this for linking to libssl.so.10 (RHEL/Fedora/CentOS)
|
||||
testCompile "io.netty:netty-tcnative:${versions.tcnative}:linux-x86_64-fedora"
|
||||
} else {
|
||||
// use this for linking to libssl.so.1.0.0
|
||||
testCompile "io.netty:netty-tcnative:${versions.tcnative}:linux-x86_64"
|
||||
}
|
||||
}
|
||||
alpnboot "org.mortbay.jetty.alpn:alpn-boot:${versions.alpnboot}"
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
showStandardStreams = true
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
// note: bootstrapClasspath does not use /p (or /a)
|
||||
jvmArgs "-Xbootclasspath/p:" + configurations.alpnboot.asPath
|
||||
systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
|
||||
systemProperty 'io.netty.leakDetection.level', 'advanced'
|
||||
}
|
323
oai-client/config/checkstyle/checkstyle.xml
Normal file
323
oai-client/config/checkstyle/checkstyle.xml
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!-- This is a checkstyle configuration file. For descriptions of
|
||||
what the following rules do, please see the checkstyle configuration
|
||||
page at http://checkstyle.sourceforge.net/config.html -->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<!-- Checks that there are no tab characters in the file.
|
||||
-->
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||
by an open paren.)
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="JavadocPackage">
|
||||
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||
Only allows a package-info.java, not package.html. -->
|
||||
</module>
|
||||
|
||||
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!--
|
||||
|
||||
IMPORT CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="RedundantImport">
|
||||
<!-- Checks for redundant import statements. -->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ImportOrder">
|
||||
<!-- Checks for out of order import statements. -->
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="groups" value="com,junit,net,org,java,javax"/>
|
||||
<!-- This ensures that static imports go first. -->
|
||||
<property name="option" value="top"/>
|
||||
<property name="tokens" value="STATIC_IMPORT, IMPORT"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
JAVADOC CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
<property name="allowUndeclaredRTE" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocStyle">
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
NAMING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||
|
||||
<module name="PackageName">
|
||||
<!-- Validates identifiers for package names against the
|
||||
supplied expression. -->
|
||||
<!-- Here the default checkstyle rule restricts package name parts to
|
||||
seven characters, this is not in line with common practice at Google.
|
||||
-->
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="TypeNameCheck">
|
||||
<!-- Validates static, final fields against the
|
||||
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||
<metadata name="altname" value="TypeName"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ConstantNameCheck">
|
||||
<!-- Validates non-private, static, final fields against the supplied
|
||||
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||
<metadata name="altname" value="ConstantName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="false"/>
|
||||
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="StaticVariableNameCheck">
|
||||
<!-- Validates static, non-final fields against the supplied
|
||||
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||
<metadata name="altname" value="StaticVariableName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberNameCheck">
|
||||
<!-- Validates non-static members against the supplied expression. -->
|
||||
<metadata name="altname" value="MemberName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MethodNameCheck">
|
||||
<!-- Validates identifiers for method names. -->
|
||||
<metadata name="altname" value="MethodName"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ParameterName">
|
||||
<!-- Validates identifiers for method parameters against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalFinalVariableName">
|
||||
<!-- Validates identifiers for local final variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalVariableName">
|
||||
<!-- Validates identifiers for local variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
LENGTH and CODING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="LineLength">
|
||||
<!-- Checks if a line is too long. -->
|
||||
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<!--
|
||||
The default ignore pattern exempts the following elements:
|
||||
- import statements
|
||||
- long URLs inside comments
|
||||
-->
|
||||
|
||||
<property name="ignorePattern"
|
||||
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||
</module>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||
the same line. e.g., the following example is fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
} else
|
||||
</pre>
|
||||
-->
|
||||
<!-- This next example is not fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
}
|
||||
else
|
||||
</pre>
|
||||
-->
|
||||
<property name="option" value="same"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for braces around if and else blocks -->
|
||||
<module name="NeedBraces">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<module name="UpperEll">
|
||||
<!-- Checks that long constants are defined with an upper ell.-->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="FallThrough">
|
||||
<!-- Warn about falling through to the next case statement. Similar to
|
||||
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||
some other variants which we don't publicized to promote consistency).
|
||||
-->
|
||||
<property name="reliefPattern"
|
||||
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
MODIFIERS CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="ModifierOrder">
|
||||
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||
8.4.3. The prescribed order is:
|
||||
public, protected, private, abstract, static, final, transient, volatile,
|
||||
synchronized, native, strictfp
|
||||
-->
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
WHITESPACE CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<!-- Checks that various tokens are surrounded by whitespace.
|
||||
This includes most binary operators and keywords followed
|
||||
by regular or curly braces.
|
||||
-->
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter">
|
||||
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||
whitespace.
|
||||
-->
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<!-- Checks that there is no whitespace after various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||
UNARY_PLUS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceBefore">
|
||||
<!-- Checks that there is no whitespace before various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ParenPad">
|
||||
<!-- Checks that there is no whitespace before close parens or after
|
||||
open parens.
|
||||
-->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.OAIRequest;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
import org.xbib.oai.util.URIBuilder;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Client OAI request
|
||||
*/
|
||||
public class ClientOAIRequest implements OAIRequest {
|
||||
|
||||
private URIBuilder uriBuilder;
|
||||
|
||||
private DateTimeFormatter dateTimeFormatter;
|
||||
|
||||
private ResumptionToken<?> token;
|
||||
|
||||
private String set;
|
||||
|
||||
private String metadataPrefix;
|
||||
|
||||
private Instant from;
|
||||
|
||||
private Instant until;
|
||||
|
||||
private boolean retry;
|
||||
|
||||
protected ClientOAIRequest() {
|
||||
uriBuilder = new URIBuilder();
|
||||
}
|
||||
|
||||
public void setURL(URL url) {
|
||||
try {
|
||||
URI uri = url.toURI();
|
||||
uriBuilder.scheme(uri.getScheme())
|
||||
.authority(uri.getAuthority())
|
||||
.path(uri.getPath());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("invalid URI " + url);
|
||||
}
|
||||
}
|
||||
|
||||
public URL getURL() throws MalformedURLException {
|
||||
return uriBuilder.build().toURL();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return uriBuilder.buildGetPath();
|
||||
}
|
||||
|
||||
public void addParameter(String name, String value) {
|
||||
if (value != null && !value.isEmpty()) {
|
||||
uriBuilder.addParameter(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSet(String set) {
|
||||
this.set = set;
|
||||
addParameter(OAIConstants.SET_PARAMETER, set);
|
||||
}
|
||||
|
||||
public String getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadataPrefix(String prefix) {
|
||||
this.metadataPrefix = prefix;
|
||||
addParameter(OAIConstants.METADATA_PREFIX_PARAMETER, prefix);
|
||||
}
|
||||
|
||||
public String getMetadataPrefix() {
|
||||
return metadataPrefix;
|
||||
}
|
||||
|
||||
public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter) {
|
||||
this.dateTimeFormatter = dateTimeFormatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrom(Instant from) {
|
||||
this.from = from;
|
||||
String fromStr = dateTimeFormatter == null ? from.toString() : dateTimeFormatter.format(from);
|
||||
addParameter(OAIConstants.FROM_PARAMETER, fromStr);
|
||||
}
|
||||
|
||||
public Instant getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUntil(Instant until) {
|
||||
this.until = until;
|
||||
String untilStr = dateTimeFormatter == null ? until.toString() : dateTimeFormatter.format(until);
|
||||
addParameter(OAIConstants.UNTIL_PARAMETER, untilStr);
|
||||
}
|
||||
|
||||
public Instant getUntil() {
|
||||
return until;
|
||||
}
|
||||
|
||||
public void setResumptionToken(ResumptionToken<?> token) {
|
||||
this.token = token;
|
||||
if (token != null && token.toString() != null) {
|
||||
// resumption token may have characters that are illegal in URIs like '|'
|
||||
//String tokenStr = URIFormatter.encode(token.toString(), StandardCharsets.UTF_8);
|
||||
addParameter(OAIConstants.RESUMPTION_TOKEN_PARAMETER, token.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public ResumptionToken<?> getResumptionToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setRetry(boolean retry) {
|
||||
this.retry = retry;
|
||||
}
|
||||
|
||||
public boolean isRetry() {
|
||||
return retry;
|
||||
}
|
||||
|
||||
class GetRecord extends ClientOAIRequest {
|
||||
|
||||
public GetRecord() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.GET_RECORD);
|
||||
}
|
||||
}
|
||||
|
||||
class Identify extends ClientOAIRequest {
|
||||
|
||||
public Identify() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.IDENTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
class ListIdentifiers extends ClientOAIRequest {
|
||||
|
||||
public ListIdentifiers() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.LIST_IDENTIFIERS);
|
||||
}
|
||||
}
|
||||
|
||||
class ListMetadataFormats extends ClientOAIRequest {
|
||||
|
||||
public ListMetadataFormats() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.LIST_METADATA_FORMATS);
|
||||
}
|
||||
}
|
||||
|
||||
class ListRecordsRequest extends ClientOAIRequest {
|
||||
|
||||
public ListRecordsRequest() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.LIST_RECORDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ListSetsRequest extends ClientOAIRequest {
|
||||
|
||||
public ListSetsRequest() {
|
||||
addParameter(OAIConstants.VERB_PARAMETER, OAIConstants.LIST_SETS);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.OAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Default OAI response
|
||||
*/
|
||||
public interface ClientOAIResponse extends OAIResponse {
|
||||
|
||||
void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException;
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.xbib.helianthus.client.ClientBuilder;
|
||||
import org.xbib.helianthus.client.ClientFactory;
|
||||
import org.xbib.helianthus.client.http.HttpClient;
|
||||
import org.xbib.oai.client.getrecord.GetRecordRequest;
|
||||
import org.xbib.oai.client.identify.IdentifyRequest;
|
||||
import org.xbib.oai.client.listidentifiers.ListIdentifiersRequest;
|
||||
import org.xbib.oai.client.listmetadataformats.ListMetadataFormatsRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsRequest;
|
||||
import org.xbib.oai.client.listsets.ListSetsRequest;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
|
||||
/**
|
||||
* Default OAI client
|
||||
*/
|
||||
public class DefaultOAIClient implements OAIClient {
|
||||
|
||||
private HttpClient client;
|
||||
|
||||
private ClientFactory clientFactory;
|
||||
|
||||
private URL url;
|
||||
|
||||
@Override
|
||||
public DefaultOAIClient setURL(URL url) throws URISyntaxException {
|
||||
return setURL(url, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOAIClient setURL(URL url, boolean trustAlways) throws URISyntaxException {
|
||||
this.url = url;
|
||||
this.clientFactory = ClientFactory.DEFAULT;
|
||||
this.client = new ClientBuilder("none+" + url.toURI())
|
||||
.factory(clientFactory)
|
||||
.defaultResponseTimeout(Duration.ofMinutes(1L)) // maybe not enough for extreme slow archive servers...
|
||||
.build(HttpClient.class);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientFactory getFactory() {
|
||||
return clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifyRequest newIdentifyRequest() {
|
||||
IdentifyRequest request = new IdentifyRequest();
|
||||
request.setURL(url);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListMetadataFormatsRequest newListMetadataFormatsRequest() {
|
||||
ListMetadataFormatsRequest request = new ListMetadataFormatsRequest();
|
||||
request.setURL(getURL());
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListSetsRequest newListSetsRequest() {
|
||||
ListSetsRequest request = new ListSetsRequest();
|
||||
request.setURL(getURL());
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIdentifiersRequest newListIdentifiersRequest() {
|
||||
ListIdentifiersRequest request = new ListIdentifiersRequest();
|
||||
request.setURL(getURL());
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetRecordRequest newGetRecordRequest() {
|
||||
GetRecordRequest request = new GetRecordRequest();
|
||||
request.setURL(getURL());
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListRecordsRequest newListRecordsRequest() {
|
||||
ListRecordsRequest request = new ListRecordsRequest();
|
||||
request.setURL(getURL());
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifyRequest resume(IdentifyRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newIdentifyRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListRecordsRequest resume(ListRecordsRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newListRecordsRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIdentifiersRequest resume(ListIdentifiersRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newListIdentifiersRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListMetadataFormatsRequest resume(ListMetadataFormatsRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newListMetadataFormatsRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListSetsRequest resume(ListSetsRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newListSetsRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetRecordRequest resume(GetRecordRequest request, ResumptionToken<?> token) {
|
||||
if (request.isRetry()) {
|
||||
request.setRetry(false);
|
||||
return request;
|
||||
}
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
request = newGetRecordRequest();
|
||||
request.setResumptionToken(token);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
}
|
107
oai-client/src/main/java/org/xbib/oai/client/OAIClient.java
Normal file
107
oai-client/src/main/java/org/xbib/oai/client/OAIClient.java
Normal file
|
@ -0,0 +1,107 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.xbib.helianthus.client.ClientFactory;
|
||||
import org.xbib.helianthus.client.http.HttpClient;
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.OAISession;
|
||||
import org.xbib.oai.client.getrecord.GetRecordRequest;
|
||||
import org.xbib.oai.client.identify.IdentifyRequest;
|
||||
import org.xbib.oai.client.listidentifiers.ListIdentifiersRequest;
|
||||
import org.xbib.oai.client.listmetadataformats.ListMetadataFormatsRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsRequest;
|
||||
import org.xbib.oai.client.listsets.ListSetsRequest;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
|
||||
/**
|
||||
* OAI client API
|
||||
*
|
||||
*/
|
||||
public interface OAIClient extends OAISession, OAIConstants {
|
||||
|
||||
OAIClient setURL(URL uri, boolean trustAlways) throws URISyntaxException;
|
||||
|
||||
OAIClient setURL(URL uri) throws URISyntaxException;
|
||||
|
||||
URL getURL();
|
||||
|
||||
HttpClient getHttpClient();
|
||||
|
||||
ClientFactory getFactory();
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve information about a repository.
|
||||
* Some of the information returned is required as part of the OAI-PMH.
|
||||
* Repositories may also employ the Identify verb to return additional
|
||||
* descriptive information.
|
||||
* @return identify request
|
||||
*/
|
||||
IdentifyRequest newIdentifyRequest();
|
||||
|
||||
IdentifyRequest resume(IdentifyRequest request, ResumptionToken<?> token);
|
||||
|
||||
/**
|
||||
* This verb is an abbreviated form of ListRecords, retrieving only
|
||||
* headers rather than records. Optional arguments permit selective
|
||||
* harvesting of headers based on set membership and/or datestamp.
|
||||
* Depending on the repository's support for deletions, a returned
|
||||
* header may have a status attribute of "deleted" if a record
|
||||
* matching the arguments specified in the request has been deleted.
|
||||
* @return list identifiers request
|
||||
*
|
||||
*/
|
||||
ListIdentifiersRequest newListIdentifiersRequest();
|
||||
|
||||
ListIdentifiersRequest resume(ListIdentifiersRequest request, ResumptionToken<?> token);
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve the metadata formats available
|
||||
* from a repository. An optional argument restricts the request
|
||||
* to the formats available for a specific item.
|
||||
* @return list metadata formats request
|
||||
*/
|
||||
ListMetadataFormatsRequest newListMetadataFormatsRequest();
|
||||
|
||||
ListMetadataFormatsRequest resume(ListMetadataFormatsRequest request, ResumptionToken<?> token);
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve the set structure of a repository,
|
||||
* useful for selective harvesting.
|
||||
* @return list sets request
|
||||
*/
|
||||
ListSetsRequest newListSetsRequest();
|
||||
|
||||
ListSetsRequest resume(ListSetsRequest request, ResumptionToken<?> token);
|
||||
|
||||
/**
|
||||
* This verb is used to harvest records from a repository.
|
||||
* Optional arguments permit selective harvesting of records based on
|
||||
* set membership and/or datestamp. Depending on the repository's
|
||||
* support for deletions, a returned header may have a status
|
||||
* attribute of "deleted" if a record matching the arguments
|
||||
* specified in the request has been deleted. No metadata
|
||||
* will be present for records with deleted status.
|
||||
* @return list records request
|
||||
*/
|
||||
ListRecordsRequest newListRecordsRequest();
|
||||
|
||||
ListRecordsRequest resume(ListRecordsRequest request, ResumptionToken<?> token);
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve an individual metadata record from
|
||||
* a repository. Required arguments specify the identifier of the item
|
||||
* from which the record is requested and the format of the metadata
|
||||
* that should be included in the record. Depending on the level at
|
||||
* which a repository tracks deletions, a header with a "deleted" value
|
||||
* for the status attribute may be returned, in case the metadata format
|
||||
* specified by the metadataPrefix is no longer available from the
|
||||
* repository or from the specified item.
|
||||
* @return get record request
|
||||
*/
|
||||
GetRecordRequest newGetRecordRequest();
|
||||
|
||||
GetRecordRequest resume(GetRecordRequest request, ResumptionToken<?> token);
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* Factory for OAI clients
|
||||
*
|
||||
*/
|
||||
public class OAIClientFactory {
|
||||
|
||||
private final static OAIClientFactory instance = new OAIClientFactory();
|
||||
|
||||
private OAIClientFactory() {
|
||||
}
|
||||
|
||||
public static OAIClientFactory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static OAIClient newClient() {
|
||||
return new DefaultOAIClient();
|
||||
}
|
||||
|
||||
public static OAIClient newClient(String spec) {
|
||||
return newClient(spec, false);
|
||||
}
|
||||
|
||||
public static OAIClient newClient(String spec, boolean trustAll) {
|
||||
Properties properties = new Properties();
|
||||
InputStream in = instance.getClass().getResourceAsStream("/org/xbib/oai/client/" + spec + ".properties");
|
||||
if (in != null) {
|
||||
try {
|
||||
properties.load(in);
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
DefaultOAIClient client = new DefaultOAIClient();
|
||||
try {
|
||||
client.setURL(new URL(properties.getProperty("uri")), trustAll);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return client;
|
||||
} else {
|
||||
DefaultOAIClient client = new DefaultOAIClient();
|
||||
try {
|
||||
client.setURL(new URL(spec));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.client.getrecord;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GetRecordRequest extends ClientOAIRequest {
|
||||
|
||||
public GetRecordRequest() {
|
||||
super();
|
||||
addParameter(VERB_PARAMETER, GET_RECORD);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.xbib.oai.client.getrecord;
|
||||
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GetRecordResponse implements ClientOAIResponse {
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI get record verb.
|
||||
*/
|
||||
package org.xbib.oai.client.getrecord;
|
|
@ -0,0 +1,15 @@
|
|||
package org.xbib.oai.client.identify;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
import org.xbib.oai.OAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IdentifyRequest extends ClientOAIRequest implements OAIRequest {
|
||||
|
||||
public IdentifyRequest() {
|
||||
super();
|
||||
addParameter(VERB_PARAMETER, IDENTIFY);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package org.xbib.oai.client.identify;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IdentifyResponse implements ClientOAIResponse {
|
||||
|
||||
private String repositoryName;
|
||||
|
||||
private URL baseURL;
|
||||
|
||||
private String protocolVersion;
|
||||
|
||||
private List<String> adminEmails = new ArrayList<>();
|
||||
|
||||
private Date earliestDatestamp;
|
||||
|
||||
private String deletedRecord;
|
||||
|
||||
private String granularity;
|
||||
|
||||
private String compression;
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
InputSource is = new InputSource(new StringReader(message.content().toStringUtf8()));
|
||||
Document doc = db.parse(is);
|
||||
setGranularity(getString("granularity", doc.getDocumentElement()));
|
||||
} catch (ParserConfigurationException | SAXException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
}
|
||||
|
||||
public void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
public String getRepositoryName() {
|
||||
return repositoryName;
|
||||
}
|
||||
|
||||
public void setBaseURL(URL url) {
|
||||
this.baseURL = url;
|
||||
}
|
||||
|
||||
public URL getBaseURL() {
|
||||
return baseURL;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(String protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public String getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void addAdminEmail(String email) {
|
||||
adminEmails.add(email);
|
||||
}
|
||||
|
||||
public List<String> getAdminEmails() {
|
||||
return adminEmails;
|
||||
}
|
||||
|
||||
public void setEarliestDatestamp(Date earliestDatestamp) {
|
||||
this.earliestDatestamp = earliestDatestamp;
|
||||
}
|
||||
|
||||
public Date getEarliestDatestamp() {
|
||||
return earliestDatestamp;
|
||||
}
|
||||
|
||||
public void setDeletedRecord(String deletedRecord) {
|
||||
this.deletedRecord = deletedRecord;
|
||||
}
|
||||
|
||||
public String getDeleteRecord() {
|
||||
return deletedRecord;
|
||||
}
|
||||
|
||||
public void setGranularity(String granularity) {
|
||||
this.granularity = granularity;
|
||||
}
|
||||
|
||||
public String getGranularity() {
|
||||
return granularity;
|
||||
}
|
||||
|
||||
public void setCompression(String compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public String getCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
private String getString(String tagName, Element element) {
|
||||
NodeList list = element.getElementsByTagName(tagName);
|
||||
if (list != null && list.getLength() > 0) {
|
||||
NodeList subList = list.item(0).getChildNodes();
|
||||
if (subList != null && subList.getLength() > 0) {
|
||||
return subList.item(0).getNodeValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI identify verb.
|
||||
*/
|
||||
package org.xbib.oai.client.identify;
|
|
@ -0,0 +1,15 @@
|
|||
package org.xbib.oai.client.listidentifiers;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
import org.xbib.oai.OAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListIdentifiersRequest extends ClientOAIRequest implements OAIRequest {
|
||||
|
||||
public ListIdentifiersRequest() {
|
||||
super();
|
||||
addParameter(VERB_PARAMETER, LIST_IDENTIFIERS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.xbib.oai.client.listidentifiers;
|
||||
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListIdentifiersResponse implements ClientOAIResponse {
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list identifiers verb.
|
||||
*/
|
||||
package org.xbib.oai.client.listidentifiers;
|
|
@ -0,0 +1,16 @@
|
|||
package org.xbib.oai.client.listmetadataformats;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
import org.xbib.oai.OAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListMetadataFormatsRequest extends ClientOAIRequest implements OAIRequest {
|
||||
|
||||
public ListMetadataFormatsRequest() {
|
||||
super();
|
||||
addParameter(VERB_PARAMETER, LIST_METADATA_FORMATS);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.xbib.oai.client.listmetadataformats;
|
||||
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListMetadataFormatsResponse implements ClientOAIResponse {
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list metadata formats verb.
|
||||
*/
|
||||
package org.xbib.oai.client.listmetadataformats;
|
|
@ -0,0 +1,215 @@
|
|||
package org.xbib.oai.client.listrecords;
|
||||
|
||||
import org.xbib.content.xml.util.XMLFilterReader;
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.util.RecordHeader;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
import org.xbib.oai.xml.MetadataHandler;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListRecordsFilterReader extends XMLFilterReader {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ListRecordsFilterReader.class.getName());
|
||||
|
||||
private final ListRecordsRequest request;
|
||||
|
||||
private final ListRecordsResponse response;
|
||||
|
||||
private StringBuilder content;
|
||||
|
||||
private RecordHeader header;
|
||||
|
||||
private ResumptionToken<String> token;
|
||||
|
||||
private boolean inMetadata;
|
||||
|
||||
ListRecordsFilterReader(ListRecordsRequest request, ListRecordsResponse response) {
|
||||
super();
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.content = new StringBuilder();
|
||||
this.inMetadata = false;
|
||||
}
|
||||
|
||||
public ResumptionToken<String> getResumptionToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public ListRecordsResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
logger.log(Level.FINE, "start of document");
|
||||
super.startDocument();
|
||||
request.setResumptionToken(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
logger.log(Level.FINE, "end of document");
|
||||
super.endDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localname, String qname, Attributes atts) throws SAXException {
|
||||
super.startElement(uri, localname, qname, atts);
|
||||
if (OAIConstants.NS_URI.equals(uri)) {
|
||||
switch (localname) {
|
||||
case "header":
|
||||
header = new RecordHeader();
|
||||
break;
|
||||
case "error":
|
||||
response.setError(atts.getValue("code"));
|
||||
break;
|
||||
case "metadata":
|
||||
inMetadata = true;
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.startDocument();
|
||||
}
|
||||
break;
|
||||
case "resumptionToken":
|
||||
try {
|
||||
token = ResumptionToken.newToken(null);
|
||||
String cursor = atts.getValue("cursor");
|
||||
if (cursor != null) {
|
||||
token.setCursor(Integer.parseInt(cursor));
|
||||
}
|
||||
String completeListSize = atts.getValue("completeListSize");
|
||||
if (completeListSize != null) {
|
||||
token.setCompleteListSize(Integer.parseInt(completeListSize));
|
||||
}
|
||||
if (!token.isComplete()) {
|
||||
request.setResumptionToken(token);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (inMetadata) {
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.startElement(uri, localname, qname, atts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String nsURI, String localname, String qname) throws SAXException {
|
||||
super.endElement(nsURI, localname, qname);
|
||||
if (OAIConstants.NS_URI.equals(nsURI)) {
|
||||
switch (localname) {
|
||||
case "header":
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.setHeader(header);
|
||||
}
|
||||
header = new RecordHeader();
|
||||
break;
|
||||
case "metadata":
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.endDocument();
|
||||
}
|
||||
inMetadata = false;
|
||||
break;
|
||||
case "responseDate":
|
||||
response.setDate(Instant.parse(content.toString().trim()));
|
||||
break;
|
||||
case "resumptionToken":
|
||||
if (token != null && content != null && content.length() > 0) {
|
||||
token.setValue(content.toString());
|
||||
// feedback to request
|
||||
request.setResumptionToken(token);
|
||||
} else {
|
||||
logger.log(Level.WARNING, "empty resumption token value");
|
||||
// some servers send a null or an empty token as last token
|
||||
token = null;
|
||||
request.setResumptionToken(null);
|
||||
}
|
||||
break;
|
||||
case "identifier":
|
||||
if (header != null && content != null && content.length() > 0) {
|
||||
String id = content.toString().trim();
|
||||
header.setIdentifier(id);
|
||||
}
|
||||
break;
|
||||
case "datestamp":
|
||||
if (header != null && content != null && content.length() > 0) {
|
||||
try {
|
||||
header.setDate(Instant.parse(content.toString().trim()));
|
||||
} catch (DateTimeParseException e) {
|
||||
// not "seconds ISO"
|
||||
}
|
||||
try {
|
||||
LocalDateTime ldt = LocalDateTime.parse(content.toString().trim(),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
header.setDate(Instant.from(ldt));
|
||||
} catch (DateTimeParseException e) {
|
||||
// not "day ISO"
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "setSpec":
|
||||
if (header != null && content != null && content.length() > 0) {
|
||||
header.setSetspec(content.toString().trim());
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (content != null) {
|
||||
content.setLength(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (inMetadata) {
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.endElement(nsURI, localname, qname);
|
||||
}
|
||||
}
|
||||
content.setLength(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int start, int length) throws SAXException {
|
||||
super.characters(chars, start, length);
|
||||
content.append(new String(chars, start, length).trim());
|
||||
if (inMetadata) {
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.characters(chars, start, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String uri) throws SAXException {
|
||||
super.startPrefixMapping(prefix, uri);
|
||||
if (inMetadata) {
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.startPrefixMapping(prefix, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String prefix) throws SAXException {
|
||||
super.endPrefixMapping(prefix);
|
||||
if (inMetadata) {
|
||||
for (MetadataHandler mh : request.getHandlers()) {
|
||||
mh.endPrefixMapping(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.xbib.oai.client.listrecords;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.xml.MetadataHandler;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListRecordsRequest extends ClientOAIRequest {
|
||||
|
||||
private List<MetadataHandler> handlers = new LinkedList<>();
|
||||
|
||||
public ListRecordsRequest() {
|
||||
super();
|
||||
addParameter(OAIConstants.VERB_PARAMETER, LIST_RECORDS);
|
||||
}
|
||||
public ListRecordsRequest addHandler(MetadataHandler handler) {
|
||||
handlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<MetadataHandler> getHandlers() { return handlers; }
|
||||
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package org.xbib.oai.client.listrecords;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.util.AsciiString;
|
||||
import org.xbib.content.xml.transform.TransformerURIResolver;
|
||||
import org.xbib.content.xml.util.XMLUtil;
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
import org.xbib.oai.exceptions.BadArgumentException;
|
||||
import org.xbib.oai.exceptions.BadResumptionTokenException;
|
||||
import org.xbib.oai.exceptions.NoRecordsMatchException;
|
||||
import org.xbib.oai.exceptions.OAIException;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.sax.SAXSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.Writer;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListRecordsResponse implements ClientOAIResponse {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ListRecordsResponse.class.getName());
|
||||
private static final String[] RETRY_AFTER_HEADERS = {
|
||||
"retry-after", "Retry-after", "Retry-After"
|
||||
};
|
||||
|
||||
private final ListRecordsRequest request;
|
||||
|
||||
private ListRecordsFilterReader filterreader;
|
||||
|
||||
private long retryAfterMillis;
|
||||
|
||||
private String error;
|
||||
|
||||
private Instant date;
|
||||
|
||||
public ListRecordsResponse(ListRecordsRequest request) {
|
||||
this.request = request;
|
||||
this.retryAfterMillis = 20 * 1000; // 20 seconds by default
|
||||
}
|
||||
|
||||
public ListRecordsResponse setRetryAfter(long millis) {
|
||||
this.retryAfterMillis = millis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setDate(Instant date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public Instant getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
String content = message.content().toStringUtf8();
|
||||
int status = message.status().code();
|
||||
if (status == 503) {
|
||||
long secs = retryAfterMillis / 1000;
|
||||
if (message.headers() != null) {
|
||||
for (String retryAfterHeader : RETRY_AFTER_HEADERS) {
|
||||
String retryAfter = message.headers().get(AsciiString.of(retryAfterHeader));
|
||||
if (retryAfter == null) {
|
||||
continue;
|
||||
}
|
||||
secs = Long.parseLong(retryAfter);
|
||||
if (!isDigits(retryAfter)) {
|
||||
// parse RFC date, e.g. Fri, 31 Dec 1999 23:59:59 GMT
|
||||
Instant instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(retryAfter));
|
||||
secs = ChronoUnit.SECONDS.between(instant, Instant.now());
|
||||
logger.log(Level.INFO, MessageFormat.format("parsed delay seconds is {0}", secs));
|
||||
}
|
||||
logger.log(Level.INFO, MessageFormat.format("setting delay seconds to {0}", secs));
|
||||
}
|
||||
}
|
||||
request.setRetry(true);
|
||||
try {
|
||||
if (secs > 0L) {
|
||||
logger.log(Level.INFO, MessageFormat.format("waiting for {0} seconds (retry-after)", secs));
|
||||
Thread.sleep(1000 * secs);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.log(Level.SEVERE, "interrupted");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (status != 200) {
|
||||
throw new IOException("status = " + status + " response = " + content);
|
||||
}
|
||||
// activate XSLT only if OAI XML content type is returned
|
||||
String contentType = message.headers().get(HttpHeaderNames.CONTENT_TYPE);
|
||||
if (contentType != null && !contentType.startsWith("text/xml")) {
|
||||
throw new IOException("no XML content type in response: " + contentType);
|
||||
}
|
||||
this.filterreader = new ListRecordsFilterReader(request, this);
|
||||
try {
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
transformerFactory.setURIResolver(new TransformerURIResolver("xsl"));
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
Source source = new SAXSource(filterreader, new InputSource(new StringReader(XMLUtil.sanitize(content))));
|
||||
StreamResult streamResult = new StreamResult(writer);
|
||||
logger.log(Level.FINE, "transforming");
|
||||
transformer.transform(source, streamResult);
|
||||
if ("noRecordsMatch".equals(error)) {
|
||||
throw new NoRecordsMatchException("metadataPrefix=" + request.getMetadataPrefix()
|
||||
+ ",set=" + request.getSet()
|
||||
+ ",from=" + request.getFrom()
|
||||
+ ",until=" + request.getUntil());
|
||||
} else if ("badResumptionToken".equals(error)) {
|
||||
throw new BadResumptionTokenException(request.getResumptionToken());
|
||||
} else if ("badArgument".equals(error)) {
|
||||
throw new BadArgumentException();
|
||||
} else if (error != null) {
|
||||
throw new OAIException(error);
|
||||
}
|
||||
} catch (TransformerException t) {
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
}
|
||||
|
||||
private boolean isDigits(String str) {
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
if (!Character.isDigit(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ResumptionToken<?> getResumptionToken() {
|
||||
return filterreader != null ? filterreader.getResumptionToken() : null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list records verb.
|
||||
*/
|
||||
package org.xbib.oai.client.listrecords;
|
|
@ -0,0 +1,15 @@
|
|||
package org.xbib.oai.client.listsets;
|
||||
|
||||
import org.xbib.oai.client.ClientOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListSetsRequest extends ClientOAIRequest {
|
||||
|
||||
public ListSetsRequest() {
|
||||
super();
|
||||
addParameter(VERB_PARAMETER, LIST_SETS);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.xbib.oai.client.listsets;
|
||||
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.oai.client.ClientOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListSetsResponse implements ClientOAIResponse {
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(AggregatedHttpMessage message, Writer writer) throws IOException {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list sets verb.
|
||||
*/
|
||||
package org.xbib.oai.client.listsets;
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for OAI client.
|
||||
*/
|
||||
package org.xbib.oai.client;
|
|
@ -0,0 +1,124 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
import org.xbib.helianthus.client.http.HttpClient;
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.helianthus.common.http.HttpHeaderNames;
|
||||
import org.xbib.helianthus.common.http.HttpHeaders;
|
||||
import org.xbib.helianthus.common.http.HttpMethod;
|
||||
import org.xbib.oai.client.identify.IdentifyRequest;
|
||||
import org.xbib.oai.client.identify.IdentifyResponse;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsResponse;
|
||||
import org.xbib.oai.xml.SimpleMetadataHandler;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ArxivClientTest {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(ArxivClientTest.class.getName());
|
||||
|
||||
@Test
|
||||
public void testListRecordsArxiv() throws Exception {
|
||||
try {
|
||||
OAIClient client = OAIClientFactory.newClient("http://export.arxiv.org/oai2");
|
||||
IdentifyRequest identifyRequest = client.newIdentifyRequest();
|
||||
HttpClient httpClient = client.getHttpClient();
|
||||
AggregatedHttpMessage response = httpClient.execute(HttpHeaders.of(HttpMethod.GET, identifyRequest.getPath())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
IdentifyResponse identifyResponse = new IdentifyResponse();
|
||||
identifyResponse.receivedResponse(response, new StringWriter());
|
||||
String granularity = identifyResponse.getGranularity();
|
||||
logger.info("granularity = {}", granularity);
|
||||
DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null;
|
||||
// ArXiv wants us to wait 20 secs between *every* HTTP request, so we must wait here
|
||||
logger.info("waiting 20 seconds");
|
||||
Thread.sleep(20 * 1000L);
|
||||
ListRecordsRequest listRecordsRequest = client.newListRecordsRequest();
|
||||
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter);
|
||||
listRecordsRequest.setFrom(Instant.parse("2016-11-01T00:00:00Z"));
|
||||
listRecordsRequest.setUntil(Instant.parse("2016-11-02T00:00:00Z"));
|
||||
listRecordsRequest.setMetadataPrefix("arXiv");
|
||||
final AtomicLong count = new AtomicLong(0L);
|
||||
SimpleMetadataHandler simpleMetadataHandler = new SimpleMetadataHandler() {
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
logger.debug("start doc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
logger.debug("end doc");
|
||||
count.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String uri) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String prefix) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname, Attributes atrbts) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String ns, String localname, String qname) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int pos, int len) throws SAXException {
|
||||
}
|
||||
|
||||
};
|
||||
File file = File.createTempFile("arxiv.", ".xml");
|
||||
file.deleteOnExit();
|
||||
FileWriter fileWriter = new FileWriter(file);
|
||||
while (listRecordsRequest != null) {
|
||||
try {
|
||||
listRecordsRequest.addHandler(simpleMetadataHandler);
|
||||
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
|
||||
logger.info("sending {}", listRecordsRequest.getPath());
|
||||
response = httpClient.execute(HttpHeaders.of(HttpMethod.GET, listRecordsRequest.getPath())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
logger.debug("response headers = {} resumption-token = {}",
|
||||
response.headers(), listRecordsResponse.getResumptionToken());
|
||||
listRecordsResponse.receivedResponse(response, fileWriter);
|
||||
listRecordsRequest = client.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
listRecordsRequest = null;
|
||||
}
|
||||
}
|
||||
fileWriter.close();
|
||||
client.close();
|
||||
logger.info("count={}", count.get());
|
||||
assertTrue(count.get() > 0L);
|
||||
} catch (ConnectException | ExecutionException e) {
|
||||
logger.warn("skipped, can not connect", e);
|
||||
} catch (InterruptedException | IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
115
oai-client/src/test/java/org/xbib/oai/client/DNBClientTest.java
Normal file
115
oai-client/src/test/java/org/xbib/oai/client/DNBClientTest.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
import org.xbib.helianthus.client.http.HttpClient;
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.helianthus.common.http.HttpHeaderNames;
|
||||
import org.xbib.helianthus.common.http.HttpHeaders;
|
||||
import org.xbib.helianthus.common.http.HttpMethod;
|
||||
import org.xbib.oai.client.identify.IdentifyRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsResponse;
|
||||
import org.xbib.oai.xml.SimpleMetadataHandler;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DNBClientTest {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(DNBClientTest.class.getName());
|
||||
|
||||
@Test
|
||||
public void testIdentify() throws Exception {
|
||||
OAIClient client = OAIClientFactory.newClient("http://services.dnb.de/oai/repository");
|
||||
IdentifyRequest request = client.newIdentifyRequest();
|
||||
HttpClient httpClient = client.getHttpClient();
|
||||
assertEquals("/oai/repository?verb=Identify", request.getPath());
|
||||
AggregatedHttpMessage response = httpClient.get(request.getPath()).aggregate().get();
|
||||
logger.info("{}", response.content().toStringUtf8());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListRecordsDNB() throws Exception {
|
||||
try {
|
||||
OAIClient client = OAIClientFactory.newClient("http://services.dnb.de/oai/repository");
|
||||
ListRecordsRequest listRecordsRequest = client.newListRecordsRequest();
|
||||
listRecordsRequest.setFrom(Instant.parse("2016-01-01T00:00:00Z"));
|
||||
listRecordsRequest.setUntil(Instant.parse("2016-01-10T00:00:00Z"));
|
||||
listRecordsRequest.setSet("bib");
|
||||
listRecordsRequest.setMetadataPrefix("PicaPlus-xml");
|
||||
final AtomicLong count = new AtomicLong(0L);
|
||||
SimpleMetadataHandler simpleMetadataHandler = new SimpleMetadataHandler() {
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
logger.debug("startDocument");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
count.incrementAndGet();
|
||||
logger.debug("endDocument");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String uri) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String prefix) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname, Attributes atrbts) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String ns, String localname, String qname) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int pos, int len) throws SAXException {
|
||||
}
|
||||
|
||||
};
|
||||
File file = File.createTempFile("dnb-bib-pica.", ".xml");
|
||||
file.deleteOnExit();
|
||||
FileWriter sw = new FileWriter(file);
|
||||
while (listRecordsRequest != null) {
|
||||
try {
|
||||
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
|
||||
listRecordsRequest.addHandler(simpleMetadataHandler);
|
||||
HttpClient httpClient = client.getHttpClient();
|
||||
AggregatedHttpMessage response = httpClient.execute(HttpHeaders.of(HttpMethod.GET, listRecordsRequest.getPath())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
String content = response.content().toStringUtf8();
|
||||
listRecordsResponse.receivedResponse(response, sw);
|
||||
listRecordsRequest = client.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
listRecordsRequest = null;
|
||||
}
|
||||
}
|
||||
sw.close();
|
||||
client.close();
|
||||
logger.info("count={}", count.get());
|
||||
} catch (ConnectException | ExecutionException e) {
|
||||
logger.warn("skipped, can not connect");
|
||||
} catch (IOException e) {
|
||||
logger.warn("skipped, HTTP exception");
|
||||
}
|
||||
}
|
||||
}
|
142
oai-client/src/test/java/org/xbib/oai/client/DOAJClientTest.java
Normal file
142
oai-client/src/test/java/org/xbib/oai/client/DOAJClientTest.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
package org.xbib.oai.client;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
import org.xbib.helianthus.client.Clients;
|
||||
import org.xbib.helianthus.client.http.HttpClient;
|
||||
import org.xbib.helianthus.common.http.AggregatedHttpMessage;
|
||||
import org.xbib.helianthus.common.http.HttpHeaderNames;
|
||||
import org.xbib.helianthus.common.http.HttpHeaders;
|
||||
import org.xbib.helianthus.common.http.HttpMethod;
|
||||
import org.xbib.oai.client.identify.IdentifyRequest;
|
||||
import org.xbib.oai.client.identify.IdentifyResponse;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsRequest;
|
||||
import org.xbib.oai.client.listrecords.ListRecordsResponse;
|
||||
import org.xbib.oai.xml.SimpleMetadataHandler;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DOAJClientTest {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(DOAJClientTest.class.getName());
|
||||
|
||||
@Test
|
||||
public void testListRecordsDOAJ() throws InterruptedException, TimeoutException, IOException {
|
||||
try {
|
||||
// will redirect to https://doaj.org/oai
|
||||
OAIClient oaiClient = OAIClientFactory.newClient("http://doaj.org/oai", true);
|
||||
IdentifyRequest identifyRequest = oaiClient.newIdentifyRequest();
|
||||
HttpClient client = oaiClient.getHttpClient();
|
||||
AggregatedHttpMessage response = client.execute(HttpHeaders.of(HttpMethod.GET, identifyRequest.getPath())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
// follow a maximum of 10 HTTP redirects
|
||||
int max = 10;
|
||||
while (response.followUrl() != null && max-- > 0) {
|
||||
URI uri = URI.create(response.followUrl());
|
||||
client = Clients.newClient(oaiClient.getFactory(), "none+" + uri, HttpClient.class);
|
||||
response = client.execute(HttpHeaders.of(HttpMethod.GET, response.followUrl())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
}
|
||||
IdentifyResponse identifyResponse = new IdentifyResponse();
|
||||
String content = response.content().toStringUtf8();
|
||||
logger.debug("identifyResponse = {}", content);
|
||||
identifyResponse.receivedResponse(response, new StringWriter());
|
||||
String granularity = identifyResponse.getGranularity();
|
||||
logger.info("granularity = {}", granularity);
|
||||
DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null;
|
||||
ListRecordsRequest listRecordsRequest = oaiClient.newListRecordsRequest();
|
||||
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter);
|
||||
listRecordsRequest.setFrom(Instant.parse("2016-01-06T00:00:00Z"));
|
||||
listRecordsRequest.setUntil(Instant.parse("2016-11-07T00:00:00Z"));
|
||||
listRecordsRequest.setMetadataPrefix("oai_dc");
|
||||
final AtomicLong count = new AtomicLong(0L);
|
||||
SimpleMetadataHandler simpleMetadataHandler = new SimpleMetadataHandler() {
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
logger.debug("start doc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
logger.debug("end doc");
|
||||
count.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String uri) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String prefix) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname, Attributes atrbts) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String ns, String localname, String qname) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int pos, int len) throws SAXException {
|
||||
}
|
||||
};
|
||||
File file = File.createTempFile("doaj.", ".xml");
|
||||
file.deleteOnExit();
|
||||
FileWriter fileWriter = new FileWriter(file);
|
||||
do {
|
||||
try {
|
||||
listRecordsRequest.addHandler(simpleMetadataHandler);
|
||||
client = oaiClient.getHttpClient();
|
||||
response = client.execute(HttpHeaders.of(HttpMethod.GET, listRecordsRequest.getPath())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
// follow a maximum of 10 HTTP redirects
|
||||
max = 10;
|
||||
while (response.followUrl() != null && max-- > 0) {
|
||||
URI uri = URI.create(response.followUrl());
|
||||
client = Clients.newClient(oaiClient.getFactory(), "none+" + uri, HttpClient.class);
|
||||
response = client.execute(HttpHeaders.of(HttpMethod.GET, response.followUrl())
|
||||
.set(HttpHeaderNames.ACCEPT, "utf-8")).aggregate().get();
|
||||
}
|
||||
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
|
||||
logger.debug("response = {}", response.headers());
|
||||
listRecordsResponse.receivedResponse(response, fileWriter);
|
||||
listRecordsRequest = oaiClient.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
listRecordsRequest = null;
|
||||
}
|
||||
} while (listRecordsRequest != null);
|
||||
fileWriter.close();
|
||||
oaiClient.close();
|
||||
logger.info("count={}", count.get());
|
||||
assertTrue(count.get() > 0L);
|
||||
} catch (ConnectException | ExecutionException e) {
|
||||
logger.warn("skipped, can not connect, exception is:", e);
|
||||
} catch (InterruptedException | IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for testing OAI client.
|
||||
*/
|
||||
package org.xbib.oai.client;
|
13
oai-client/src/test/resources/log4j2.xml
Normal file
13
oai-client/src/test/resources/log4j2.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="OFF">
|
||||
<appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%d{ABSOLUTE}][%-5p][%-25c][%t] %m%n"/>
|
||||
</Console>
|
||||
</appenders>
|
||||
<Loggers>
|
||||
<Root level="debug">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</configuration>
|
|
@ -0,0 +1 @@
|
|||
uri=http://services.dnb.de/oai/repository
|
|
@ -0,0 +1 @@
|
|||
uri=http://doaj.org/oai
|
|
@ -0,0 +1 @@
|
|||
uri=http://services.dnb.de/oai/repository
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
|
||||
|
||||
<responseDate>2013-06-19T07:11:37Z</responseDate>
|
||||
<request verb="ListRecords" from="2013-01-01T00:00:00Z" until="2013-01-02T00:00:00Z" metadataPrefix="oai_dc">http://doaj.org/oai</request>
|
||||
|
||||
<ListRecords>
|
||||
<record>
|
||||
<header>
|
||||
<identifier>oai:doaj.org:2011-9860</identifier>
|
||||
<datestamp>2013-01-01T12:30:30Z</datestamp>
|
||||
<setSpec>Biology_and_Life_Sciences</setSpec>
|
||||
</header>
|
||||
<metadata>
|
||||
<oai_dc:dc xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
|
||||
<dc:title>Morfolia</dc:title>
|
||||
<dc:identifier>http://www.revistas.unal.edu.co/index.php/morfolia</dc:identifier>
|
||||
<dc:identifier>http://www.doaj.org/doaj?func=openurl&genre=journal&issn=20119860</dc:identifier>
|
||||
<dc:identifier>issn: 2011-9860</dc:identifier>
|
||||
<dc:publisher>Universidad Nacional de Colombia</dc:publisher>
|
||||
<dc:date>2008</dc:date>
|
||||
<dc:language>Spanish</dc:language>
|
||||
<dc:subject>morphology</dc:subject>
|
||||
<dc:subject>anatomy</dc:subject>
|
||||
<dc:subject>histology</dc:subject>
|
||||
<dc:subject>embryology</dc:subject>
|
||||
<dc:subject>genetics</dc:subject>
|
||||
<dc:subject>DoajSubjectTerm: Biology
|
||||
</dc:subject>
|
||||
<dc:subject>LCC: QH301-705.5</dc:subject>
|
||||
</oai_dc:dc>
|
||||
</metadata>
|
||||
</record>
|
||||
<record>
|
||||
<header>
|
||||
<identifier>oai:doaj.org:1091-1774</identifier>
|
||||
<datestamp>2013-01-01T01:02:00Z</datestamp>
|
||||
<setSpec>Social_Sciences</setSpec>
|
||||
</header>
|
||||
<metadata>
|
||||
<oai_dc:dc xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
|
||||
<dc:title>Hmong Studies Journal</dc:title>
|
||||
<dc:identifier>http://www.hmongstudies.org/HmongStudiesJournal.html </dc:identifier>
|
||||
<dc:identifier>http://www.doaj.org/doaj?func=openurl&genre=journal&issn=10911774</dc:identifier>
|
||||
<dc:identifier>issn: 1091-1774</dc:identifier>
|
||||
<dc:publisher>Hmong Studies Journal</dc:publisher>
|
||||
<dc:date>1996</dc:date>
|
||||
<dc:language>English</dc:language>
|
||||
<dc:subject>Hmong culture</dc:subject>
|
||||
<dc:subject>Hmong history</dc:subject>
|
||||
<dc:subject>southeast Asian Americans</dc:subject>
|
||||
<dc:subject>Asian American studies</dc:subject>
|
||||
<dc:subject>southeast Asian studies</dc:subject>
|
||||
<dc:subject>DoajSubjectTerm: Social Sciences
|
||||
</dc:subject>
|
||||
<dc:subject>LCC: H1-99</dc:subject>
|
||||
<dc:subject>LCC: HD28-9999</dc:subject>
|
||||
</oai_dc:dc>
|
||||
</metadata>
|
||||
</record>
|
||||
<resumptionToken cursor="0" completeListSize="2"/>
|
||||
</ListRecords>
|
||||
|
||||
</OAI-PMH>
|
|
@ -0,0 +1,41 @@
|
|||
# XML namespace
|
||||
xml = http://www.w3.org/XML/1998/namespace
|
||||
xsl = http://www.w3.org/1999/XSL/Transform
|
||||
|
||||
# Atom
|
||||
atom = http://www.w3.org/2005/Atom
|
||||
|
||||
# RDF namespace
|
||||
rdf = http://www.w3.org/1999/02/22-rdf-syntax-ns#
|
||||
rdfs = http://www.w3.org/2000/01/rdf-schema#
|
||||
owl = http://www.w3.org/2002/07/owl#
|
||||
|
||||
foaf = http://xmlns.com/foaf/0.1/
|
||||
|
||||
# Apache
|
||||
xalan = http://xml.apache.org/xslt
|
||||
|
||||
# Dublin Core Namespaces
|
||||
# http://dublincore.org/documents/dcmi-namespace/
|
||||
dc = http://purl.org/dc/elements/1.1/
|
||||
dcterms = http://purl.org/dc/terms/
|
||||
dcam = http://purl.org/dc/dcam/
|
||||
dcmitype http://purl.org/dc/dcmitype/
|
||||
rel = http://purl.org/vocab/relationship/
|
||||
|
||||
# Library of Congress
|
||||
marcrel = http://www.loc.gov/loc.terms/relators/
|
||||
mods = http://www.loc.gov/mods/v3
|
||||
bib = info:srw/cql-context-set/1/bib-v1/
|
||||
|
||||
# RDA, MARC
|
||||
rdagr2 = http://RDVocab.info/ElementsGr2/
|
||||
marclang = http://marccodes.heroku.com/languages/
|
||||
|
||||
# DNB
|
||||
gnd = http://d-nb.info/standards/elementset/gnd#
|
||||
gndvocab = http://d-nb.info/standards/vocab/gnd/
|
||||
|
||||
# xbib
|
||||
xbib = http://xbib.org/elements/
|
||||
|
659
oai-client/src/test/resources/xsl/oai2.xsl
Normal file
659
oai-client/src/test/resources/xsl/oai2.xsl
Normal file
|
@ -0,0 +1,659 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
XSL Transform to convert OAI 2.0 responses into XHTML
|
||||
|
||||
By Christopher Gutteridge, University of Southampton
|
||||
|
||||
v1.1
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
Copyright (c) 2006 University of Southampton, UK. SO17 1BJ.
|
||||
|
||||
EPrints 3 is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EPrints 3 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EPrints 3; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
All the elements really needed for EPrints are done but if
|
||||
you want to use this XSL for other OAI archive you may want
|
||||
to make some minor changes or additions.
|
||||
|
||||
Not Done
|
||||
The 'about' section of 'record'
|
||||
The 'compession' part of 'identify'
|
||||
The optional attributes of 'resumptionToken'
|
||||
The optional 'setDescription' container of 'set'
|
||||
|
||||
All the links just link to oai_dc versions of records.
|
||||
|
||||
-->
|
||||
<xsl:stylesheet
|
||||
version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:oai="http://www.openarchives.org/OAI/2.0/"
|
||||
>
|
||||
|
||||
<xsl:output method="html"/>
|
||||
|
||||
|
||||
|
||||
<xsl:template name="style">
|
||||
td.value {
|
||||
vertical-align: top;
|
||||
padding-left: 1em;
|
||||
padding: 3px;
|
||||
}
|
||||
td.key {
|
||||
background-color: #e0e0ff;
|
||||
padding: 3px;
|
||||
text-align: right;
|
||||
border: 1px solid #c0c0c0;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
}
|
||||
.dcdata td.key {
|
||||
background-color: #ffffe0;
|
||||
}
|
||||
body {
|
||||
margin: 1em 2em 1em 2em;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-family: sans-serif;
|
||||
clear: left;
|
||||
}
|
||||
h1 {
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 0.3em;
|
||||
font-size: medium;
|
||||
}
|
||||
.link {
|
||||
border: 1px outset #88f;
|
||||
background-color: #c0c0ff;
|
||||
padding: 1px 4px 1px 4px;
|
||||
font-size: 80%;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
color: black;
|
||||
}
|
||||
.link:hover {
|
||||
color: red;
|
||||
}
|
||||
.link:active {
|
||||
color: red;
|
||||
border: 1px inset #88f;
|
||||
background-color: #a0a0df;
|
||||
}
|
||||
.oaiRecord, .oaiRecordTitle {
|
||||
background-color: #f0f0ff;
|
||||
border-style: solid;
|
||||
border-color: #d0d0d0;
|
||||
}
|
||||
h2.oaiRecordTitle {
|
||||
background-color: #e0e0ff;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
border-width: 2px 2px 0px 2px;
|
||||
margin: 0px;
|
||||
}
|
||||
.oaiRecord {
|
||||
margin-bottom: 3em;
|
||||
border-width: 2px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.results {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
ul.quicklinks {
|
||||
margin-top: 2px;
|
||||
padding: 4px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #ccc;
|
||||
border-top: 2px solid #ccc;
|
||||
clear: left;
|
||||
}
|
||||
ul.quicklinks li {
|
||||
font-size: 80%;
|
||||
display: inline;
|
||||
list-stlye: none;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
p.intro {
|
||||
font-size: 80%;
|
||||
}
|
||||
<xsl:call-template name='xmlstyle' />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:variable name='identifier' select="substring-before(concat(substring-after(/oai:OAI-PMH/oai:request,'identifier='),'&'),'&')" />
|
||||
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<head>
|
||||
<title>OAI 2.0 Request Results</title>
|
||||
<style><xsl:call-template name="style"/></style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>OAI 2.0 Request Results</h1>
|
||||
<xsl:call-template name="quicklinks"/>
|
||||
<p class="intro">You are viewing an HTML version of the XML OAI response. To see the underlying XML use your web browsers view source option. More information about this XSLT is at the <a href="#moreinfo">bottom of the page</a>.</p>
|
||||
<xsl:apply-templates select="/oai:OAI-PMH" />
|
||||
<xsl:call-template name="quicklinks"/>
|
||||
<h2><a name="moreinfo">About the XSLT</a></h2>
|
||||
<p>An XSLT file has converted the <a href="http://www.openarchives.org">OAI-PMH 2.0</a> responses into XHTML which looks nice in a browser which supports XSLT such as Mozilla, Firebird and Internet Explorer. The XSLT file was created by <a href="http://www.ecs.soton.ac.uk/people/cjg">Christopher Gutteridge</a> at the University of Southampton as part of the <a href="http://www.eprints.org/software/">GNU EPrints system</a>, and is freely redistributable under the <a href="http://www.gnu.org">GPL</a>.</p><p>If you want to use the XSL file on your own OAI interface you may but due to the way XSLT works you must install the XSL file on the same server as the OAI script, you can't just link to this copy.</p><p>For more information or to download the XSL file please see the <a href="http://software.eprints.org/xslt.php">OAI to XHTML XSLT homepage</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="quicklinks">
|
||||
<ul class="quicklinks">
|
||||
<li><a href="?verb=Identify">Identify</a> | </li>
|
||||
<li><a href="?verb=ListRecords&metadataPrefix=oai_dc">ListRecords</a> | </li>
|
||||
<li><a href="?verb=ListSets">ListSets</a> | </li>
|
||||
<li><a href="?verb=ListMetadataFormats">ListMetadataFormats</a> | </li>
|
||||
<li><a href="?verb=ListIdentifiers&metadataPrefix=oai_dc">ListIdentifiers</a></li>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH">
|
||||
<table class="values">
|
||||
<tr><td class="key">Datestamp of response</td>
|
||||
<td class="value"><xsl:value-of select="oai:responseDate"/></td></tr>
|
||||
<tr><td class="key">Request URL</td>
|
||||
<td class="value"><xsl:value-of select="oai:request"/></td></tr>
|
||||
</table>
|
||||
<!-- verb: [<xsl:value-of select="oai:request/@verb" />]<br /> -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="oai:error">
|
||||
<h2>OAI Error(s)</h2>
|
||||
<p>The request could not be completed due to the following error or errors.</p>
|
||||
<div class="results">
|
||||
<xsl:apply-templates select="oai:error"/>
|
||||
</div>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<p>Request was of type <xsl:value-of select="oai:request/@verb"/>.</p>
|
||||
<div class="results">
|
||||
<xsl:apply-templates select="oai:Identify" />
|
||||
<xsl:apply-templates select="oai:GetRecord"/>
|
||||
<xsl:apply-templates select="oai:ListRecords"/>
|
||||
<xsl:apply-templates select="oai:ListSets"/>
|
||||
<xsl:apply-templates select="oai:ListMetadataFormats"/>
|
||||
<xsl:apply-templates select="oai:ListIdentifiers"/>
|
||||
</div>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- ERROR -->
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH/oai:error">
|
||||
<table class="values">
|
||||
<tr><td class="key">Error Code</td>
|
||||
<td class="value"><xsl:value-of select="@code"/></td></tr>
|
||||
</table>
|
||||
<p class="error"><xsl:value-of select="." /></p>
|
||||
</xsl:template>
|
||||
|
||||
<!-- IDENTIFY -->
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH/oai:Identify">
|
||||
<table class="values">
|
||||
<tr><td class="key">Repository Name</td>
|
||||
<td class="value"><xsl:value-of select="oai:repositoryName"/></td></tr>
|
||||
<tr><td class="key">Base URL</td>
|
||||
<td class="value"><xsl:value-of select="oai:baseURL"/></td></tr>
|
||||
<tr><td class="key">Protocol Version</td>
|
||||
<td class="value"><xsl:value-of select="oai:protocolVersion"/></td></tr>
|
||||
<tr><td class="key">Earliest Datestamp</td>
|
||||
<td class="value"><xsl:value-of select="oai:earliestDatestamp"/></td></tr>
|
||||
<tr><td class="key">Deleted Record Policy</td>
|
||||
<td class="value"><xsl:value-of select="oai:deletedRecord"/></td></tr>
|
||||
<tr><td class="key">Granularity</td>
|
||||
<td class="value"><xsl:value-of select="oai:granularity"/></td></tr>
|
||||
<xsl:apply-templates select="oai:adminEmail"/>
|
||||
</table>
|
||||
<xsl:apply-templates select="oai:description"/>
|
||||
<!--no warning about unsupported descriptions -->
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH/oai:Identify/oai:adminEmail">
|
||||
<tr><td class="key">Admin Email</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
</xsl:template>
|
||||
|
||||
<!--
|
||||
Identify / Unsupported Description
|
||||
-->
|
||||
|
||||
<xsl:template match="oai:description/*" priority="-100">
|
||||
<h2>Unsupported Description Type</h2>
|
||||
<p>The XSL currently does not support this type of description.</p>
|
||||
<div class="xmlSource">
|
||||
<xsl:apply-templates select="." mode='xmlMarkup' />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / OAI-Identifier
|
||||
-->
|
||||
|
||||
<xsl:template match="id:oai-identifier" xmlns:id="http://www.openarchives.org/OAI/2.0/oai-identifier">
|
||||
<h2>OAI-Identifier</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">Scheme</td>
|
||||
<td class="value"><xsl:value-of select="id:scheme"/></td></tr>
|
||||
<tr><td class="key">Repository Identifier</td>
|
||||
<td class="value"><xsl:value-of select="id:repositoryIdentifier"/></td></tr>
|
||||
<tr><td class="key">Delimiter</td>
|
||||
<td class="value"><xsl:value-of select="id:delimiter"/></td></tr>
|
||||
<tr><td class="key">Sample OAI Identifier</td>
|
||||
<td class="value"><xsl:value-of select="id:sampleIdentifier"/></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / EPrints
|
||||
-->
|
||||
|
||||
<xsl:template match="ep:eprints" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<h2>EPrints Description</h2>
|
||||
<xsl:if test="ep:content">
|
||||
<h3>Content</h3>
|
||||
<xsl:apply-templates select="ep:content"/>
|
||||
</xsl:if>
|
||||
<xsl:if test="ep:submissionPolicy">
|
||||
<h3>Submission Policy</h3>
|
||||
<xsl:apply-templates select="ep:submissionPolicy"/>
|
||||
</xsl:if>
|
||||
<h3>Metadata Policy</h3>
|
||||
<xsl:apply-templates select="ep:metadataPolicy"/>
|
||||
<h3>Data Policy</h3>
|
||||
<xsl:apply-templates select="ep:dataPolicy"/>
|
||||
<xsl:apply-templates select="ep:comment"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="ep:content|ep:dataPolicy|ep:metadataPolicy|ep:submissionPolicy" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<xsl:if test="ep:text">
|
||||
<p><xsl:value-of select="ep:text" /></p>
|
||||
</xsl:if>
|
||||
<xsl:if test="ep:URL">
|
||||
<div><a href="{ep:URL}"><xsl:value-of select="ep:URL" /></a></div>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="ep:comment" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<h3>Comment</h3>
|
||||
<div><xsl:value-of select="."/></div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Friends
|
||||
-->
|
||||
|
||||
<xsl:template match="fr:friends" xmlns:fr="http://www.openarchives.org/OAI/2.0/friends/">
|
||||
<h2>Friends</h2>
|
||||
<ul>
|
||||
<xsl:apply-templates select="fr:baseURL"/>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="fr:baseURL" xmlns:fr="http://www.openarchives.org/OAI/2.0/friends/">
|
||||
<li><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text>
|
||||
<a class="link" href="{.}?verb=Identify">Identify</a></li>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Branding
|
||||
-->
|
||||
|
||||
<xsl:template match="br:branding" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h2>Branding</h2>
|
||||
<xsl:apply-templates select="br:collectionIcon"/>
|
||||
<xsl:apply-templates select="br:metadataRendering"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="br:collectionIcon" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h3>Icon</h3>
|
||||
<xsl:choose>
|
||||
<xsl:when test="link!=''">
|
||||
<a href="{br:link}"><img src="{br:url}" alt="{br:title}" width="{br:width}" height="{br:height}" border="0" /></a>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<img src="{br:url}" alt="{br:title}" width="{br:width}" height="{br:height}" border="0" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="br:metadataRendering" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h3>Metadata Rendering Rule</h3>
|
||||
<table class="values">
|
||||
<tr><td class="key">URL</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
<tr><td class="key">Namespace</td>
|
||||
<td class="value"><xsl:value-of select="@metadataNamespace"/></td></tr>
|
||||
<tr><td class="key">Mime Type</td>
|
||||
<td class="value"><xsl:value-of select="@mimetype"/></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Gateway
|
||||
-->
|
||||
|
||||
<xsl:template match="gw:gateway" xmlns:gw="http://www.openarchives.org/OAI/2.0/gateway/x">
|
||||
<h2>Gateway Information</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">Source</td>
|
||||
<td class="value"><xsl:value-of select="gw:source"/></td></tr>
|
||||
<tr><td class="key">Description</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayDescription"/></td></tr>
|
||||
<xsl:apply-templates select="gw:gatewayAdmin"/>
|
||||
<xsl:if test="gw:gatewayURL">
|
||||
<tr><td class="key">URL</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayURL"/></td></tr>
|
||||
</xsl:if>
|
||||
<xsl:if test="gw:gatewayNotes">
|
||||
<tr><td class="key">Notes</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayNotes"/></td></tr>
|
||||
</xsl:if>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="gw:gatewayAdmin" xmlns:gw="http://www.openarchives.org/OAI/2.0/gateway/">
|
||||
<tr><td class="key">Admin</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- GetRecord -->
|
||||
|
||||
<xsl:template match="oai:GetRecord">
|
||||
<xsl:apply-templates select="oai:record" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListRecords -->
|
||||
|
||||
<xsl:template match="oai:ListRecords">
|
||||
<xsl:apply-templates select="oai:record" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListIdentifiers -->
|
||||
|
||||
<xsl:template match="oai:ListIdentifiers">
|
||||
<xsl:apply-templates select="oai:header" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListSets -->
|
||||
|
||||
<xsl:template match="oai:ListSets">
|
||||
<xsl:apply-templates select="oai:set" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:set">
|
||||
<h2>Set</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">setName</td>
|
||||
<td class="value"><xsl:value-of select="oai:setName"/></td></tr>
|
||||
<xsl:apply-templates select="oai:setSpec" />
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListMetadataFormats -->
|
||||
|
||||
<xsl:template match="oai:ListMetadataFormats">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$identifier">
|
||||
<p>This is a list of metadata formats available for the record "<xsl:value-of select='$identifier' />". Use these links to view the metadata: <xsl:apply-templates select="oai:metadataFormat/oai:metadataPrefix" /></p>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<p>This is a list of metadata formats available from this archive.</p>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:apply-templates select="oai:metadataFormat" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadataFormat">
|
||||
<h2>Metadata Format</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">metadataPrefix</td>
|
||||
<td class="value"><a class="link" href="?verb=ListRecords&metadataPrefix={oai:metadataPrefix}"><xsl:value-of select="oai:metadataPrefix"/></a></td></tr>
|
||||
<tr><td class="key">metadataNamespace</td>
|
||||
<td class="value"><xsl:value-of select="oai:metadataNamespace"/></td></tr>
|
||||
<tr><td class="key">schema</td>
|
||||
<td class="value"><a href="{oai:schema}"><xsl:value-of select="oai:schema"/></a></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadataPrefix">
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=GetRecord&metadataPrefix={.}&identifier={$identifier}"><xsl:value-of select='.' /></a>
|
||||
</xsl:template>
|
||||
|
||||
<!-- record object -->
|
||||
|
||||
<xsl:template match="oai:record">
|
||||
<h2 class="oaiRecordTitle">OAI Record: <xsl:value-of select="oai:header/oai:identifier"/></h2>
|
||||
<div class="oaiRecord">
|
||||
<xsl:apply-templates select="oai:header" />
|
||||
<xsl:apply-templates select="oai:metadata" />
|
||||
<xsl:apply-templates select="oai:about" />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:header">
|
||||
<h3>OAI Record Header</h3>
|
||||
<table class="values">
|
||||
<tr><td class="key">OAI Identifier</td>
|
||||
<td class="value">
|
||||
<xsl:value-of select="oai:identifier"/>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=GetRecord&metadataPrefix=oai_dc&identifier={oai:identifier}">oai_dc</a>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListMetadataFormats&identifier={oai:identifier}">formats</a>
|
||||
</td></tr>
|
||||
<tr><td class="key">Datestamp</td>
|
||||
<td class="value"><xsl:value-of select="oai:datestamp"/></td></tr>
|
||||
<xsl:apply-templates select="oai:setSpec" />
|
||||
</table>
|
||||
<xsl:if test="@status='deleted'">
|
||||
<p>This record has been deleted.</p>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template match="oai:about">
|
||||
<p>"about" part of record container not supported by the XSL</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadata">
|
||||
 
|
||||
<div class="metadata">
|
||||
<xsl:apply-templates select="*" />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- oai setSpec object -->
|
||||
|
||||
<xsl:template match="oai:setSpec">
|
||||
<tr><td class="key">setSpec</td>
|
||||
<td class="value"><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListIdentifiers&metadataPrefix=oai_dc&set={.}">Identifiers</a>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListRecords&metadataPrefix=oai_dc&set={.}">Records</a>
|
||||
</td></tr>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
|
||||
<!-- oai resumptionToken -->
|
||||
|
||||
<xsl:template match="oai:resumptionToken">
|
||||
<p>There are more results.</p>
|
||||
<table class="values">
|
||||
<tr><td class="key">resumptionToken:</td>
|
||||
<td class="value"><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text>
|
||||
<a class="link" href="?verb={/oai:OAI-PMH/oai:request/@verb}&resumptionToken={.}">Resume</a></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- unknown metadata format -->
|
||||
|
||||
<xsl:template match="oai:metadata/*" priority='-100'>
|
||||
<h3>Unknown Metadata Format</h3>
|
||||
<div class="xmlSource">
|
||||
<xsl:apply-templates select="." mode='xmlMarkup' />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<!-- oai_dc record -->
|
||||
|
||||
<xsl:template match="oai_dc:dc" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" >
|
||||
<div class="dcdata">
|
||||
<h3>Dublin Core Metadata (oai_dc)</h3>
|
||||
<table class="dcdata">
|
||||
<xsl:apply-templates select="*" />
|
||||
</table>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="dc:title" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Title</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:creator" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Author or Creator</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:subject" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Subject and Keywords</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:description" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Description</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:publisher" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Publisher</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:contributor" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Other Contributor</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:date" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Date</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:type" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Resource Type</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:format" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Format</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:identifier" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Resource Identifier</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:source" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Source</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:language" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Language</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:relation" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Relation</td><td class="value">
|
||||
<xsl:choose>
|
||||
<xsl:when test='starts-with(.,"http" )'>
|
||||
<xsl:choose>
|
||||
<xsl:when test='string-length(.) > 50'>
|
||||
<a class="link" href="{.}">URL</a>
|
||||
<i> URL not shown as it is very long.</i>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<a href="{.}"><xsl:value-of select="."/></a>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="."/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:coverage" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Coverage</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:rights" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Rights Management</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<!-- XML Pretty Maker -->
|
||||
|
||||
<xsl:template match="node()" mode='xmlMarkup'>
|
||||
<div class="xmlBlock">
|
||||
<<span class="xmlTagName"><xsl:value-of select='name(.)' /></span><xsl:apply-templates select="@*" mode='xmlMarkup'/>><xsl:apply-templates select="node()" mode='xmlMarkup' /></<span class="xmlTagName"><xsl:value-of select='name(.)' /></span>>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="text()" mode='xmlMarkup'><span class="xmlText"><xsl:value-of select='.' /></span></xsl:template>
|
||||
|
||||
<xsl:template match="@*" mode='xmlMarkup'>
|
||||
<xsl:text> </xsl:text><span class="xmlAttrName"><xsl:value-of select='name()' /></span>="<span class="xmlAttrValue"><xsl:value-of select='.' /></span>"
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="xmlstyle">
|
||||
.xmlSource {
|
||||
font-size: 70%;
|
||||
border: solid #c0c0a0 1px;
|
||||
background-color: #ffffe0;
|
||||
padding: 2em 2em 2em 0em;
|
||||
}
|
||||
.xmlBlock {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.xmlTagName {
|
||||
color: #800000;
|
||||
font-weight: bold;
|
||||
}
|
||||
.xmlAttrName {
|
||||
font-weight: bold;
|
||||
}
|
||||
.xmlAttrValue {
|
||||
color: #0000c0;
|
||||
}
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
6
oai-common/build.gradle
Normal file
6
oai-common/build.gradle
Normal file
|
@ -0,0 +1,6 @@
|
|||
dependencies {
|
||||
compile "org.xbib:content-rdf:1.0.3"
|
||||
//testCompile "xerces:xercesImpl:${versions.xerces}"
|
||||
//testCompile "xalan:xalan:${versions.xalan}"
|
||||
//testCompile "com.fasterxml.woodstox:woodstox-core:${versions.woodstox}"
|
||||
}
|
323
oai-common/config/checkstyle/checkstyle.xml
Normal file
323
oai-common/config/checkstyle/checkstyle.xml
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!-- This is a checkstyle configuration file. For descriptions of
|
||||
what the following rules do, please see the checkstyle configuration
|
||||
page at http://checkstyle.sourceforge.net/config.html -->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<!-- Checks that there are no tab characters in the file.
|
||||
-->
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||
by an open paren.)
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="JavadocPackage">
|
||||
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||
Only allows a package-info.java, not package.html. -->
|
||||
</module>
|
||||
|
||||
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!--
|
||||
|
||||
IMPORT CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="RedundantImport">
|
||||
<!-- Checks for redundant import statements. -->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ImportOrder">
|
||||
<!-- Checks for out of order import statements. -->
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="groups" value="com,junit,net,org,java,javax"/>
|
||||
<!-- This ensures that static imports go first. -->
|
||||
<property name="option" value="top"/>
|
||||
<property name="tokens" value="STATIC_IMPORT, IMPORT"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
JAVADOC CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
<property name="allowUndeclaredRTE" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocStyle">
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
NAMING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||
|
||||
<module name="PackageName">
|
||||
<!-- Validates identifiers for package names against the
|
||||
supplied expression. -->
|
||||
<!-- Here the default checkstyle rule restricts package name parts to
|
||||
seven characters, this is not in line with common practice at Google.
|
||||
-->
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="TypeNameCheck">
|
||||
<!-- Validates static, final fields against the
|
||||
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||
<metadata name="altname" value="TypeName"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ConstantNameCheck">
|
||||
<!-- Validates non-private, static, final fields against the supplied
|
||||
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||
<metadata name="altname" value="ConstantName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="false"/>
|
||||
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="StaticVariableNameCheck">
|
||||
<!-- Validates static, non-final fields against the supplied
|
||||
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||
<metadata name="altname" value="StaticVariableName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberNameCheck">
|
||||
<!-- Validates non-static members against the supplied expression. -->
|
||||
<metadata name="altname" value="MemberName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MethodNameCheck">
|
||||
<!-- Validates identifiers for method names. -->
|
||||
<metadata name="altname" value="MethodName"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ParameterName">
|
||||
<!-- Validates identifiers for method parameters against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalFinalVariableName">
|
||||
<!-- Validates identifiers for local final variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalVariableName">
|
||||
<!-- Validates identifiers for local variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
LENGTH and CODING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="LineLength">
|
||||
<!-- Checks if a line is too long. -->
|
||||
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<!--
|
||||
The default ignore pattern exempts the following elements:
|
||||
- import statements
|
||||
- long URLs inside comments
|
||||
-->
|
||||
|
||||
<property name="ignorePattern"
|
||||
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||
</module>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||
the same line. e.g., the following example is fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
} else
|
||||
</pre>
|
||||
-->
|
||||
<!-- This next example is not fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
}
|
||||
else
|
||||
</pre>
|
||||
-->
|
||||
<property name="option" value="same"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for braces around if and else blocks -->
|
||||
<module name="NeedBraces">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<module name="UpperEll">
|
||||
<!-- Checks that long constants are defined with an upper ell.-->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="FallThrough">
|
||||
<!-- Warn about falling through to the next case statement. Similar to
|
||||
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||
some other variants which we don't publicized to promote consistency).
|
||||
-->
|
||||
<property name="reliefPattern"
|
||||
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
MODIFIERS CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="ModifierOrder">
|
||||
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||
8.4.3. The prescribed order is:
|
||||
public, protected, private, abstract, static, final, transient, volatile,
|
||||
synchronized, native, strictfp
|
||||
-->
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
WHITESPACE CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<!-- Checks that various tokens are surrounded by whitespace.
|
||||
This includes most binary operators and keywords followed
|
||||
by regular or curly braces.
|
||||
-->
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter">
|
||||
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||
whitespace.
|
||||
-->
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<!-- Checks that there is no whitespace after various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||
UNARY_PLUS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceBefore">
|
||||
<!-- Checks that there is no whitespace before various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ParenPad">
|
||||
<!-- Checks that there is no whitespace before close parens or after
|
||||
open parens.
|
||||
-->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package org.xbib.oai;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <R> response type parameter
|
||||
*/
|
||||
public abstract class DefaultOAIResponseListener<R extends OAIResponse> {
|
||||
|
||||
}
|
49
oai-common/src/main/java/org/xbib/oai/OAIConstants.java
Normal file
49
oai-common/src/main/java/org/xbib/oai/OAIConstants.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
package org.xbib.oai;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface OAIConstants {
|
||||
|
||||
String USER_AGENT = "OAI/20161111";
|
||||
|
||||
String NS_URI = "http://www.openarchives.org/OAI/2.0/";
|
||||
|
||||
String NS_PREFIX = "oai";
|
||||
|
||||
String OAIDC_NS_URI = "http://www.openarchives.org/OAI/2.0/oai_dc/";
|
||||
|
||||
String OAIDC_NS_PREFIX = "oai_dc";
|
||||
|
||||
String DC_NS_URI = "http://www.purl.org/dc/elements/1.1/";
|
||||
|
||||
String DC_PREFIX = "dc";
|
||||
|
||||
String VERB_PARAMETER = "verb";
|
||||
|
||||
String IDENTIFY = "Identify";
|
||||
|
||||
String LIST_METADATA_FORMATS = "ListMetadataFormats";
|
||||
|
||||
String LIST_SETS = "ListSets";
|
||||
|
||||
String LIST_RECORDS = "ListRecords";
|
||||
|
||||
String LIST_IDENTIFIERS = "ListIdentifiers";
|
||||
|
||||
String GET_RECORD = "GetRecord";
|
||||
|
||||
String FROM_PARAMETER = "from";
|
||||
|
||||
String UNTIL_PARAMETER = "until";
|
||||
|
||||
String SET_PARAMETER = "set";
|
||||
|
||||
String METADATA_PREFIX_PARAMETER = "metadataPrefix";
|
||||
|
||||
String RESUMPTION_TOKEN_PARAMETER = "resumptionToken";
|
||||
|
||||
String IDENTIFIER_PARAMETER = "identifier";
|
||||
|
||||
String REQUEST = "request";
|
||||
}
|
23
oai-common/src/main/java/org/xbib/oai/OAIRequest.java
Normal file
23
oai-common/src/main/java/org/xbib/oai/OAIRequest.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package org.xbib.oai;
|
||||
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* OAI request API.
|
||||
*/
|
||||
public interface OAIRequest extends OAIConstants {
|
||||
|
||||
void setSet(String set);
|
||||
|
||||
void setMetadataPrefix(String prefix);
|
||||
|
||||
void setFrom(Instant from);
|
||||
|
||||
void setUntil(Instant until);
|
||||
|
||||
void setResumptionToken(ResumptionToken<?> token);
|
||||
|
||||
ResumptionToken<?> getResumptionToken();
|
||||
}
|
12
oai-common/src/main/java/org/xbib/oai/OAIResponse.java
Normal file
12
oai-common/src/main/java/org/xbib/oai/OAIResponse.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package org.xbib.oai;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* OAI response.
|
||||
*/
|
||||
public interface OAIResponse {
|
||||
|
||||
void to(Writer writer) throws IOException;
|
||||
}
|
10
oai-common/src/main/java/org/xbib/oai/OAISession.java
Normal file
10
oai-common/src/main/java/org/xbib/oai/OAISession.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/**
|
||||
* OAI session.
|
||||
*/
|
||||
public interface OAISession extends Closeable {
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BadArgumentException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = -6647892792394074500L;
|
||||
|
||||
public BadArgumentException() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public BadArgumentException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BadResumptionTokenException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 7384401627260164303L;
|
||||
|
||||
public BadResumptionTokenException(ResumptionToken<?> token) {
|
||||
super(token != null ? token.toString() : null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BadVerbException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 1642129565793325510L;
|
||||
|
||||
public BadVerbException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CannotDisseminateFormatException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 154900133710811545L;
|
||||
|
||||
public CannotDisseminateFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IdDoesNotExistException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 9201985582562843506L;
|
||||
|
||||
public IdDoesNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class NoRecordsMatchException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 5201331168058463772L;
|
||||
|
||||
public NoRecordsMatchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class NoSetHierarchyException extends OAIException {
|
||||
|
||||
private static final long serialVersionUID = 6275260694745177314L;
|
||||
|
||||
public NoSetHierarchyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.xbib.oai.exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class OAIException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = -1890146067179892744L;
|
||||
|
||||
public OAIException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public OAIException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public OAIException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI exceptions.
|
||||
*/
|
||||
package org.xbib.oai.exceptions;
|
4
oai-common/src/main/java/org/xbib/oai/package-info.java
Normal file
4
oai-common/src/main/java/org/xbib/oai/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for OAI.
|
||||
*/
|
||||
package org.xbib.oai;
|
|
@ -0,0 +1,59 @@
|
|||
package org.xbib.oai.rdf;
|
||||
|
||||
import org.xbib.content.rdf.RdfContentParams;
|
||||
import org.xbib.content.rdf.io.xml.AbstractXmlResourceHandler;
|
||||
import org.xbib.content.rdf.io.xml.XmlHandler;
|
||||
import org.xbib.content.resource.IRI;
|
||||
import org.xbib.content.resource.IRINamespaceContext;
|
||||
import org.xbib.oai.OAIConstants;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
/**
|
||||
* A default RDF resource handler for OAI.
|
||||
*/
|
||||
public class RdfResourceHandler extends AbstractXmlResourceHandler<RdfContentParams> implements OAIConstants {
|
||||
|
||||
public RdfResourceHandler(RdfContentParams params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void identify(QName name, String value, IRI identifier) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceDelimiter(QName name) {
|
||||
boolean b = OAIDC_NS_URI.equals(name.getNamespaceURI())
|
||||
&& DC_PREFIX.equals(name.getLocalPart());
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean skip(QName name) {
|
||||
boolean b = OAIDC_NS_URI.equals(name.getNamespaceURI())
|
||||
&& DC_PREFIX.equals(name.getLocalPart());
|
||||
b = b || name.getLocalPart().startsWith("@");
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToPredicate(QName parent, String content) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public Object toObject(QName parent, String content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlHandler<RdfContentParams> setNamespaceContext(IRINamespaceContext namespaceContext) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRINamespaceContext getNamespaceContext() {
|
||||
return getParams().getNamespaceContext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package org.xbib.oai.rdf;
|
||||
|
||||
import org.xbib.content.rdf.RdfContentBuilder;
|
||||
import org.xbib.content.rdf.RdfContentParams;
|
||||
import org.xbib.content.rdf.Resource;
|
||||
import org.xbib.content.rdf.internal.DefaultAnonymousResource;
|
||||
import org.xbib.content.rdf.io.xml.XmlResourceHandler;
|
||||
import org.xbib.content.resource.IRI;
|
||||
import org.xbib.content.resource.IRINamespaceContext;
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.xml.SimpleMetadataHandler;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* RDF metadata handler.
|
||||
*/
|
||||
public class RdfSimpleMetadataHandler extends SimpleMetadataHandler implements OAIConstants {
|
||||
|
||||
private RdfResourceHandler handler;
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private RdfContentBuilder<?> builder;
|
||||
|
||||
private RdfContentParams params;
|
||||
|
||||
public static IRINamespaceContext getDefaultContext() {
|
||||
IRINamespaceContext context = IRINamespaceContext.newInstance();
|
||||
context.addNamespace(DC_PREFIX, DC_NS_URI);
|
||||
context.addNamespace(OAIDC_NS_PREFIX, OAIDC_NS_URI);
|
||||
return context;
|
||||
}
|
||||
|
||||
public RdfSimpleMetadataHandler() {
|
||||
this(RdfSimpleMetadataHandler::getDefaultContext);
|
||||
}
|
||||
|
||||
public RdfSimpleMetadataHandler(RdfContentParams params) {
|
||||
this.params = params;
|
||||
this.resource = new DefaultAnonymousResource();
|
||||
// set up our default handler
|
||||
this.handler = new RdfResourceHandler(params);
|
||||
handler.setDefaultNamespace(NS_PREFIX, NS_URI);
|
||||
}
|
||||
|
||||
public IRINamespaceContext getContext() {
|
||||
return params.getNamespaceContext();
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public RdfSimpleMetadataHandler setHandler(RdfResourceHandler handler) {
|
||||
handler.setDefaultNamespace(NS_PREFIX, NS_URI);
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XmlResourceHandler<RdfContentParams> getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public RdfSimpleMetadataHandler setBuilder(RdfContentBuilder<?> builder) {
|
||||
this.builder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.startDocument();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* At the endStream of each OAI metadata, the resource context receives the identifier from
|
||||
* the metadata header. The resource context is pushed to the RDF output.
|
||||
* Any IOException is converted to a SAXException.
|
||||
*
|
||||
* @throws SAXException if SaX fails
|
||||
*/
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
String id = getHeader().getIdentifier().trim();
|
||||
if (handler != null) {
|
||||
handler.identify(null, id, null);
|
||||
resource.setId(IRI.create(id));
|
||||
handler.endDocument();
|
||||
try {
|
||||
if (builder != null) {
|
||||
builder.receive(resource);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.startPrefixMapping(prefix, namespaceURI);
|
||||
if (prefix.isEmpty()) {
|
||||
handler.setDefaultNamespace("oai", namespaceURI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String string) throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.endPrefixMapping(string);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String string2, Attributes atrbts) throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.startElement(ns, localname, string2, atrbts);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String ns, String localname, String string2) throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.endElement(ns, localname, string2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int i, int i1) throws SAXException {
|
||||
if (handler != null) {
|
||||
handler.characters(chars, i, i1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for RDF in OAI.
|
||||
*/
|
||||
package org.xbib.oai.rdf;
|
42
oai-common/src/main/java/org/xbib/oai/util/RecordHeader.java
Normal file
42
oai-common/src/main/java/org/xbib/oai/util/RecordHeader.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package org.xbib.oai.util;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RecordHeader {
|
||||
|
||||
private String identifier;
|
||||
|
||||
private Instant date;
|
||||
|
||||
private String set;
|
||||
|
||||
public RecordHeader setIdentifier(String identifier) {
|
||||
this.identifier = identifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public RecordHeader setDate(Instant date) {
|
||||
this.date = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Instant getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public RecordHeader setSetspec(String setSpec) {
|
||||
this.set = setSpec;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSetSpec() {
|
||||
return set;
|
||||
}
|
||||
}
|
171
oai-common/src/main/java/org/xbib/oai/util/ResumptionToken.java
Normal file
171
oai-common/src/main/java/org/xbib/oai/util/ResumptionToken.java
Normal file
|
@ -0,0 +1,171 @@
|
|||
package org.xbib.oai.util;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> token parameter type
|
||||
*/
|
||||
public class ResumptionToken<T> {
|
||||
|
||||
private static final int DEFAULT_INTERVAL_SIZE = 1000;
|
||||
|
||||
private static final ConcurrentHashMap<UUID, ResumptionToken<?>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private final UUID uuid;
|
||||
|
||||
private final int interval;
|
||||
|
||||
private int position;
|
||||
|
||||
private T value;
|
||||
|
||||
private Date expirationDate;
|
||||
|
||||
private int completeListSize;
|
||||
|
||||
private int cursor;
|
||||
|
||||
private String metadataPrefix;
|
||||
|
||||
private String set;
|
||||
|
||||
private Date from;
|
||||
|
||||
private Date until;
|
||||
|
||||
private boolean completed;
|
||||
|
||||
private ResumptionToken() {
|
||||
this(DEFAULT_INTERVAL_SIZE);
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
private ResumptionToken(int interval) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.position = 0;
|
||||
this.interval = interval;
|
||||
this.value = null;
|
||||
cache.put(uuid, this);
|
||||
}
|
||||
|
||||
public static <T> ResumptionToken<T> newToken(T value) {
|
||||
return new ResumptionToken<T>().setValue(value);
|
||||
}
|
||||
|
||||
public static ResumptionToken<?> get(UUID token) {
|
||||
return cache.get(token);
|
||||
}
|
||||
|
||||
public UUID getKey() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setPosition(int position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int advancePosition() {
|
||||
setPosition(position + interval);
|
||||
return getPosition();
|
||||
}
|
||||
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setValue(T value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setExpirationDate(Date date) {
|
||||
this.expirationDate = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getExpirationDate() {
|
||||
return expirationDate;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setCompleteListSize(int size) {
|
||||
this.completeListSize = size;
|
||||
completed = size < interval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getCompleteListSize() {
|
||||
return completeListSize;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setCursor(int cursor) {
|
||||
this.cursor = cursor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setMetadataPrefix(String metadataPrefix) {
|
||||
this.metadataPrefix = metadataPrefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMetadataPrefix() {
|
||||
return metadataPrefix;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setSet(String set) {
|
||||
this.set = set;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setFrom(Date from) {
|
||||
this.from = from;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public ResumptionToken<T> setUntil(Date until) {
|
||||
this.until = until;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getUntil() {
|
||||
return until;
|
||||
}
|
||||
|
||||
public void update(int completeListSize, int pageSize, int currentPage) {
|
||||
this.completeListSize = completeListSize;
|
||||
this.cursor = pageSize * currentPage;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
}
|
||||
|
230
oai-common/src/main/java/org/xbib/oai/util/URIBuilder.java
Normal file
230
oai-common/src/main/java/org/xbib/oai/util/URIBuilder.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
package org.xbib.oai.util;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class URIBuilder {
|
||||
|
||||
private URI uri;
|
||||
private String scheme;
|
||||
private String authority;
|
||||
private String path;
|
||||
private String fragment;
|
||||
private Map<String, String> params;
|
||||
|
||||
public URIBuilder() {
|
||||
this.params = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public URIBuilder(String base) {
|
||||
this(URI.create(base));
|
||||
}
|
||||
|
||||
public URIBuilder(URI base) {
|
||||
this.uri = base;
|
||||
this.scheme = uri.getScheme();
|
||||
this.authority = uri.getAuthority();
|
||||
this.path = uri.getPath();
|
||||
this.fragment = uri.getFragment();
|
||||
this.params = parseQueryString(uri);
|
||||
}
|
||||
|
||||
public URIBuilder(URI base, Charset encoding) {
|
||||
this.uri = base;
|
||||
this.params = parseQueryString(uri, encoding);
|
||||
}
|
||||
|
||||
public URIBuilder scheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder authority(String authority) {
|
||||
this.authority = authority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder fragment(String fragment) {
|
||||
this.fragment = fragment;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* This method adds a single key/value parameter to the query
|
||||
* string of a given URI. Existing keys will be overwritten.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return this URI builder
|
||||
*/
|
||||
public URIBuilder addParameter(String key, String value) {
|
||||
params.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String buildGetPath() {
|
||||
return path + (params.isEmpty() ? "" : "?" + URIFormatter.renderQueryString(params));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a single key/value parameter to the query
|
||||
* string of a given URI, URI-escaped with the given encoding.
|
||||
* Existing keys will be overwritten.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @param encoding the encoding
|
||||
* @return this URI builder
|
||||
*/
|
||||
public URIBuilder addParameter(String key, String value, Charset encoding) {
|
||||
params.put(key, URIFormatter.encode(value, encoding));
|
||||
return this;
|
||||
}
|
||||
|
||||
public URI build() {
|
||||
try {
|
||||
return new URI(scheme, authority, path, URIFormatter.renderQueryString(params), fragment);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public URI build(Charset encoding) {
|
||||
try {
|
||||
return new URI(scheme, authority, path, URIFormatter.renderQueryString(params, encoding), fragment);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses a query string and returns a map of decoded
|
||||
* request parameters. We do not rely on java.net.URI because it does not
|
||||
* decode plus characters. The encoding is UTF-8.
|
||||
*
|
||||
* @param uri the URI to examine for request parameters
|
||||
* @return a map
|
||||
*/
|
||||
public static Map<String, String> parseQueryString(URI uri) {
|
||||
return parseQueryString(uri, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses a query string and returns a map of decoded
|
||||
* request parameters. We do not rely on java.net.URI because it does not
|
||||
* decode plus characters.
|
||||
*
|
||||
* @param uri the URI to examine for request parameters
|
||||
* @param encoding the encoding
|
||||
* @return a Map
|
||||
*/
|
||||
public static Map<String, String> parseQueryString(URI uri, Charset encoding) {
|
||||
return parseQueryString(uri, encoding, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses a query string and returns a map of decoded
|
||||
* request parameters. We do not rely on java.net.URI because it does not
|
||||
* decode plus characters. A listener can process the parameters in order.
|
||||
*
|
||||
* @param uri the URI to examine for request parameters
|
||||
* @param encoding the encoding
|
||||
* @param listener a listner for processing the URI parameters in order, or null
|
||||
* @return a Map of parameters
|
||||
*/
|
||||
public static Map<String, String> parseQueryString(URI uri, Charset encoding, ParameterListener listener) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return parseQueryString(uri.getRawQuery(), encoding, listener);
|
||||
}
|
||||
|
||||
public static Map<String, String> parseQueryString(String rawQuery, Charset encoding, ParameterListener listener) {
|
||||
Map<String, String> m = new HashMap<>();
|
||||
if (rawQuery == null) {
|
||||
return m;
|
||||
}
|
||||
// we use getRawQuery because we do our decoding by ourselves
|
||||
StringTokenizer st = new StringTokenizer(rawQuery, "&");
|
||||
while (st.hasMoreTokens()) {
|
||||
String pair = st.nextToken();
|
||||
String k;
|
||||
String v;
|
||||
int pos = pair.indexOf('=');
|
||||
if (pos < 0) {
|
||||
k = pair;
|
||||
v = null;
|
||||
} else {
|
||||
k = pair.substring(0, pos);
|
||||
v = decode(pair.substring(pos + 1, pair.length()), encoding);
|
||||
}
|
||||
m.put(k, v);
|
||||
if (listener != null) {
|
||||
listener.received(k, v);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an octet according to RFC 2396. According to this spec,
|
||||
* any characters outside the range 0x20 - 0x7E must be escaped because
|
||||
* they are not printable characters, except for any characters in the
|
||||
* fragment identifier. This method will translate any escaped characters
|
||||
* back to the original.
|
||||
*
|
||||
* @param octet the octet to decode
|
||||
* @param encoding the encoding to decode into
|
||||
* @return The decoded URI
|
||||
*/
|
||||
public static String decode(String octet, Charset encoding) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean fragment = false;
|
||||
for (int i = 0; i < octet.length(); i++) {
|
||||
char ch = octet.charAt(i);
|
||||
switch (ch) {
|
||||
case '+':
|
||||
sb.append(' ');
|
||||
break;
|
||||
case '#':
|
||||
sb.append(ch);
|
||||
fragment = true;
|
||||
break;
|
||||
case '%':
|
||||
if (!fragment) {
|
||||
// fast hex decode
|
||||
sb.append((char) ((Character.digit(octet.charAt(++i), 16) << 4)
|
||||
| Character.digit(octet.charAt(++i), 16)));
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new String(sb.toString().getBytes(StandardCharsets.ISO_8859_1), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ParameterListener {
|
||||
void received(String k, String v);
|
||||
}
|
||||
}
|
138
oai-common/src/main/java/org/xbib/oai/util/URIFormatter.java
Normal file
138
oai-common/src/main/java/org/xbib/oai/util/URIFormatter.java
Normal file
|
@ -0,0 +1,138 @@
|
|||
package org.xbib.oai.util;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class URIFormatter {
|
||||
|
||||
public static String renderQueryString(Map<String, String> m) {
|
||||
return renderQueryString(m, null, false);
|
||||
}
|
||||
|
||||
public static String renderQueryString(Map<String, String> m, Charset encoding) {
|
||||
return renderQueryString(m, encoding, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes a Map of key/value elements and converts it
|
||||
* into a URL encoded querystring format.
|
||||
*
|
||||
* @param m a map of key/value arrays
|
||||
* @param encoding the charset
|
||||
* @param encode true if arameter must be encoded
|
||||
* @return a string with the URL encoded data
|
||||
*/
|
||||
public static String renderQueryString(Map<String, String> m, Charset encoding, boolean encode) {
|
||||
String key;
|
||||
String value;
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (Map.Entry<String, String> me : m.entrySet()) {
|
||||
key = me.getKey();
|
||||
value = encode ? encode(me.getValue(), encoding) : me.getValue();
|
||||
if (key != null) {
|
||||
if (out.length() > 0) {
|
||||
out.append("&");
|
||||
}
|
||||
out.append(key);
|
||||
if ((value != null) && (value.length() > 0)) {
|
||||
out.append("=").append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encode a string into URI syntax</p>
|
||||
* <p>This function applies the URI escaping rules defined in
|
||||
* section 2 of [RFC 2396], as amended by [RFC 2732], to the string
|
||||
* supplied as the first argument, which typically represents all or part
|
||||
* of a URI, URI reference or IRI. The effect of the function is to
|
||||
* replace any special character in the string by an escape sequence of
|
||||
* the form %xx%yy..., where xxyy... is the hexadecimal representation of
|
||||
* the octets used to represent the character in US-ASCII for characters
|
||||
* in the ASCII repertoire, and a different character encoding for
|
||||
* non-ASCII characters.</p>
|
||||
* <p>If the second argument is true, all characters are escaped
|
||||
* other than lower case letters a-z, upper case letters A-Z, digits 0-9,
|
||||
* and the characters referred to in [RFC 2396] as "marks": specifically,
|
||||
* "-" | "_" | "." | "!" | "~" | "" | "'" | "(" | ")". The "%" character
|
||||
* itself is escaped only if it is not followed by two hexadecimal digits
|
||||
* (that is, 0-9, a-f, and A-F).</p>
|
||||
* <p>[RFC 2396] does not define whether escaped URIs should use
|
||||
* lower case or upper case for hexadecimal digits. To ensure that escaped
|
||||
* URIs can be compared using string comparison functions, this function
|
||||
* must always use the upper-case letters A-F.</p>
|
||||
* <p>The character encoding used as the basis for determining the
|
||||
* octets depends on the setting of the second argument.</p>
|
||||
*
|
||||
* @param s the String to convert
|
||||
* @param encoding The encoding to use for unsafe characters
|
||||
* @return The converted String
|
||||
*/
|
||||
public static String encode(String s, Charset encoding) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
int length = s.length();
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
while (true) {
|
||||
while ((i < length) && isSafe(s.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
// Safe character can just be added
|
||||
result.append(s.substring(start, i));
|
||||
// Are we done?
|
||||
if (i >= length) {
|
||||
return result.toString();
|
||||
} else if (s.charAt(i) == ' ') {
|
||||
result.append('+'); // Replace space char with plus symbol.
|
||||
i++;
|
||||
} else {
|
||||
// Get all unsafe characters
|
||||
start = i;
|
||||
char c;
|
||||
while ((i < length) && ((c = s.charAt(i)) != ' ') && !isSafe(c)) {
|
||||
i++;
|
||||
}
|
||||
// Convert them to %XY encoded strings
|
||||
String unsafe = s.substring(start, i);
|
||||
byte[] bytes = unsafe.getBytes(encoding);
|
||||
for (byte aByte : bytes) {
|
||||
result.append('%');
|
||||
result.append(hex.charAt(((int) aByte & 0xf0) >> 4));
|
||||
result.append(hex.charAt((int) aByte & 0x0f));
|
||||
}
|
||||
}
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given char is
|
||||
* either a uppercase or lowercase letter from 'a' till 'z', or a digit
|
||||
* froim '0' till '9', or one of the characters '-', '_', '.' or ''. Such
|
||||
* 'safe' character don't have to be url encoded.
|
||||
*
|
||||
* @param c the character
|
||||
* @return true or false
|
||||
*/
|
||||
private static boolean isSafe(char c) {
|
||||
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))
|
||||
|| ((c >= '0') && (c <= '9')) || (c == '-') || (c == '_') || (c == '.') || (c == '*'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert to hex. We don't use Integer.toHexString, since
|
||||
* it converts to lower case (and the Sun docs pretty clearly specify
|
||||
* upper case here), and because it doesn't provide a leading 0.
|
||||
*/
|
||||
private static final String hex = "0123456789ABCDEF";
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI utilities.
|
||||
*/
|
||||
package org.xbib.oai.util;
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.oai.xml;
|
||||
|
||||
import org.xbib.oai.util.RecordHeader;
|
||||
import org.xml.sax.ContentHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface MetadataHandler extends ContentHandler {
|
||||
|
||||
MetadataHandler setHeader(RecordHeader header);
|
||||
|
||||
RecordHeader getHeader();
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.xbib.oai.xml;
|
||||
|
||||
import org.xbib.content.xml.util.XMLFilterReader;
|
||||
import org.xbib.oai.util.RecordHeader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SimpleMetadataHandler extends XMLFilterReader implements MetadataHandler {
|
||||
|
||||
private RecordHeader header;
|
||||
|
||||
public SimpleMetadataHandler setHeader(RecordHeader header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecordHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
package org.xbib.oai.xml;
|
||||
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.Locator;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import javax.xml.stream.Location;
|
||||
import javax.xml.stream.XMLEventFactory;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.Namespace;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class XmlSimpleMetadataHandler extends SimpleMetadataHandler implements OAIConstants {
|
||||
|
||||
private final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
|
||||
|
||||
private final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
|
||||
|
||||
private List<String> namespaces = new ArrayList<>();
|
||||
|
||||
private Stack<Collection<?>> nsStack = new Stack<>();
|
||||
|
||||
private Locator locator;
|
||||
|
||||
private XMLEventWriter eventWriter;
|
||||
|
||||
private Writer writer;
|
||||
|
||||
private String id;
|
||||
|
||||
private boolean needToCallStartDocument = false;
|
||||
|
||||
public XmlSimpleMetadataHandler setWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
try {
|
||||
outputFactory.setProperty("javax.xml.stream.isRepairingNamespaces", Boolean.TRUE);
|
||||
this.eventWriter = outputFactory.createXMLEventWriter(writer);
|
||||
} catch (XMLStreamException e) {
|
||||
// ignore
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Writer getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public XmlSimpleMetadataHandler setEventWriter(XMLEventWriter eventWriter) {
|
||||
this.eventWriter = eventWriter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLEventWriter getEventWriter() {
|
||||
return eventWriter;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocumentLocator(Locator locator) {
|
||||
this.locator = locator;
|
||||
}
|
||||
|
||||
public Location getCurrentLocation() {
|
||||
if (locator != null) {
|
||||
return new SAXLocation(locator);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
namespaces.clear();
|
||||
nsStack.clear();
|
||||
eventFactory.setLocation(getCurrentLocation());
|
||||
needToCallStartDocument = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
this.id = getHeader().getIdentifier().trim();
|
||||
try {
|
||||
eventFactory.setLocation(getCurrentLocation());
|
||||
eventWriter.add(eventFactory.createEndDocument());
|
||||
} catch (XMLStreamException e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
namespaces.clear();
|
||||
nsStack.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
if (prefix == null) {
|
||||
prefix = "";
|
||||
} else if (prefix.equals("xml")) {
|
||||
return;
|
||||
}
|
||||
if (namespaces == null) {
|
||||
namespaces = new ArrayList<>();
|
||||
}
|
||||
namespaces.add(prefix);
|
||||
namespaces.add(namespaceURI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPrefixMapping(String string) throws SAXException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localname, String qname, Attributes attributes) throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
if (needToCallStartDocument) {
|
||||
try {
|
||||
eventWriter.add(eventFactory.createStartDocument());
|
||||
} catch (XMLStreamException e) {
|
||||
// is thrown because of document encoding - commented out
|
||||
//throw new SAXException(e);
|
||||
}
|
||||
needToCallStartDocument = false;
|
||||
}
|
||||
Collection<?>[] events = {null, null};
|
||||
createStartEvents(attributes, events);
|
||||
nsStack.add(events[0]);
|
||||
try {
|
||||
String[] q = {null, null};
|
||||
parseQName(qname, q);
|
||||
eventFactory.setLocation(getCurrentLocation());
|
||||
eventWriter.add(eventFactory.createStartElement(q[0], uri,
|
||||
q[1], events[1].iterator(), events[0].iterator()));
|
||||
} catch (XMLStreamException e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localname, String qname) throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
String[] q = {null, null};
|
||||
parseQName(qname, q);
|
||||
Collection<?> nsList = nsStack.remove(nsStack.size() - 1);
|
||||
Iterator<?> nsIter = nsList.iterator();
|
||||
try {
|
||||
eventFactory.setLocation(getCurrentLocation());
|
||||
eventWriter.add(eventFactory.createEndElement(q[0], uri, q[1], nsIter));
|
||||
} catch (XMLStreamException e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] chars, int i, int i1) throws SAXException {
|
||||
if (eventWriter == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
eventFactory.setLocation(getCurrentLocation());
|
||||
eventWriter.add(eventFactory.createCharacters(new String(chars, i, i1)));
|
||||
} catch (XMLStreamException e) {
|
||||
throw new SAXException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createStartEvents(Attributes attributes, Collection<?>[] events) {
|
||||
Map<String, Namespace> nsMap = null;
|
||||
List<Attribute> attrs = null;
|
||||
if (namespaces != null) {
|
||||
final int nDecls = namespaces.size();
|
||||
for (int i = 0; i < nDecls; i++) {
|
||||
final String prefix = namespaces.get(i);
|
||||
String uri = namespaces.get(i++);
|
||||
Namespace ns = createNamespace(prefix, uri);
|
||||
if (nsMap == null) {
|
||||
nsMap = new HashMap<>();
|
||||
}
|
||||
nsMap.put(prefix, ns);
|
||||
}
|
||||
}
|
||||
String[] qname = {null, null};
|
||||
for (int i = 0, s = attributes.getLength(); i < s; i++) {
|
||||
parseQName(attributes.getQName(i), qname);
|
||||
String attrPrefix = qname[0];
|
||||
String attrLocal = qname[1];
|
||||
String attrQName = attributes.getQName(i);
|
||||
String attrValue = attributes.getValue(i);
|
||||
String attrURI = attributes.getURI(i);
|
||||
if ("xmlns".equals(attrQName) || "xmlns".equals(attrPrefix)) {
|
||||
if (!attrValue.isEmpty() && nsMap != null && !nsMap.containsKey(attrPrefix)) {
|
||||
Namespace ns = createNamespace(attrPrefix, attrValue);
|
||||
nsMap = new HashMap<>();
|
||||
nsMap.put(attrPrefix, ns);
|
||||
}
|
||||
} else {
|
||||
Attribute attribute;
|
||||
if (attrPrefix.length() > 0 && !attrValue.isEmpty()) {
|
||||
attribute = eventFactory.createAttribute(attrPrefix, attrURI, attrLocal, attrValue);
|
||||
} else {
|
||||
attribute = eventFactory.createAttribute(attrLocal, attrValue);
|
||||
}
|
||||
if (attrs == null) {
|
||||
attrs = new ArrayList<>();
|
||||
}
|
||||
attrs.add(attribute);
|
||||
}
|
||||
}
|
||||
events[0] = nsMap == null ? Collections.EMPTY_LIST : nsMap.values();
|
||||
events[1] = attrs == null ? Collections.EMPTY_LIST : attrs;
|
||||
}
|
||||
|
||||
private void parseQName(String qName, String[] results) {
|
||||
String prefix, local;
|
||||
int idx = qName.indexOf(':');
|
||||
if (idx >= 0) {
|
||||
prefix = qName.substring(0, idx);
|
||||
local = qName.substring(idx + 1);
|
||||
} else {
|
||||
prefix = "";
|
||||
local = qName;
|
||||
}
|
||||
results[0] = prefix;
|
||||
results[1] = local;
|
||||
}
|
||||
|
||||
private Namespace createNamespace(String prefix, String uri) {
|
||||
if (prefix == null || prefix.length() == 0) {
|
||||
return eventFactory.createNamespace(uri);
|
||||
} else {
|
||||
return eventFactory.createNamespace(prefix, uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SAXLocation implements Location {
|
||||
private int lineNumber;
|
||||
private int columnNumber;
|
||||
private String publicId;
|
||||
private String systemId;
|
||||
|
||||
private SAXLocation(Locator locator) {
|
||||
lineNumber = locator.getLineNumber();
|
||||
columnNumber = locator.getColumnNumber();
|
||||
publicId = locator.getPublicId();
|
||||
systemId = locator.getSystemId();
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public int getColumnNumber() {
|
||||
return columnNumber;
|
||||
}
|
||||
|
||||
public int getCharacterOffset() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getPublicId() {
|
||||
return publicId;
|
||||
}
|
||||
|
||||
public String getSystemId() {
|
||||
return systemId;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for OAI XML processing.
|
||||
*/
|
||||
package org.xbib.oai.xml;
|
4
oai-server/build.gradle
Normal file
4
oai-server/build.gradle
Normal file
|
@ -0,0 +1,4 @@
|
|||
dependencies {
|
||||
compile project(':oai-common')
|
||||
compile "org.xbib.helianthus:helianthus-server:1.0.3"
|
||||
}
|
323
oai-server/config/checkstyle/checkstyle.xml
Normal file
323
oai-server/config/checkstyle/checkstyle.xml
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!-- This is a checkstyle configuration file. For descriptions of
|
||||
what the following rules do, please see the checkstyle configuration
|
||||
page at http://checkstyle.sourceforge.net/config.html -->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<!-- Checks that there are no tab characters in the file.
|
||||
-->
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||
by an open paren.)
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="JavadocPackage">
|
||||
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||
Only allows a package-info.java, not package.html. -->
|
||||
</module>
|
||||
|
||||
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!--
|
||||
|
||||
IMPORT CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="RedundantImport">
|
||||
<!-- Checks for redundant import statements. -->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ImportOrder">
|
||||
<!-- Checks for out of order import statements. -->
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="groups" value="com,junit,net,org,java,javax"/>
|
||||
<!-- This ensures that static imports go first. -->
|
||||
<property name="option" value="top"/>
|
||||
<property name="tokens" value="STATIC_IMPORT, IMPORT"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
JAVADOC CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
<property name="allowUndeclaredRTE" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocStyle">
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
NAMING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||
|
||||
<module name="PackageName">
|
||||
<!-- Validates identifiers for package names against the
|
||||
supplied expression. -->
|
||||
<!-- Here the default checkstyle rule restricts package name parts to
|
||||
seven characters, this is not in line with common practice at Google.
|
||||
-->
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="TypeNameCheck">
|
||||
<!-- Validates static, final fields against the
|
||||
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||
<metadata name="altname" value="TypeName"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ConstantNameCheck">
|
||||
<!-- Validates non-private, static, final fields against the supplied
|
||||
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||
<metadata name="altname" value="ConstantName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="false"/>
|
||||
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="StaticVariableNameCheck">
|
||||
<!-- Validates static, non-final fields against the supplied
|
||||
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||
<metadata name="altname" value="StaticVariableName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberNameCheck">
|
||||
<!-- Validates non-static members against the supplied expression. -->
|
||||
<metadata name="altname" value="MemberName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MethodNameCheck">
|
||||
<!-- Validates identifiers for method names. -->
|
||||
<metadata name="altname" value="MethodName"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ParameterName">
|
||||
<!-- Validates identifiers for method parameters against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalFinalVariableName">
|
||||
<!-- Validates identifiers for local final variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalVariableName">
|
||||
<!-- Validates identifiers for local variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
LENGTH and CODING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="LineLength">
|
||||
<!-- Checks if a line is too long. -->
|
||||
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<!--
|
||||
The default ignore pattern exempts the following elements:
|
||||
- import statements
|
||||
- long URLs inside comments
|
||||
-->
|
||||
|
||||
<property name="ignorePattern"
|
||||
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||
</module>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||
the same line. e.g., the following example is fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
} else
|
||||
</pre>
|
||||
-->
|
||||
<!-- This next example is not fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
}
|
||||
else
|
||||
</pre>
|
||||
-->
|
||||
<property name="option" value="same"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for braces around if and else blocks -->
|
||||
<module name="NeedBraces">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<module name="UpperEll">
|
||||
<!-- Checks that long constants are defined with an upper ell.-->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="FallThrough">
|
||||
<!-- Warn about falling through to the next case statement. Similar to
|
||||
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||
some other variants which we don't publicized to promote consistency).
|
||||
-->
|
||||
<property name="reliefPattern"
|
||||
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
MODIFIERS CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="ModifierOrder">
|
||||
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||
8.4.3. The prescribed order is:
|
||||
public, protected, private, abstract, static, final, transient, volatile,
|
||||
synchronized, native, strictfp
|
||||
-->
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
WHITESPACE CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<!-- Checks that various tokens are surrounded by whitespace.
|
||||
This includes most binary operators and keywords followed
|
||||
by regular or curly braces.
|
||||
-->
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter">
|
||||
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||
whitespace.
|
||||
-->
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<!-- Checks that there is no whitespace after various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||
UNARY_PLUS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceBefore">
|
||||
<!-- Checks that there is no whitespace before various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ParenPad">
|
||||
<!-- Checks that there is no whitespace before close parens or after
|
||||
open parens.
|
||||
-->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
|
120
oai-server/src/main/java/org/xbib/oai/server/OAIServer.java
Normal file
120
oai-server/src/main/java/org/xbib/oai/server/OAIServer.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
package org.xbib.oai.server;
|
||||
|
||||
import org.xbib.oai.OAISession;
|
||||
import org.xbib.oai.exceptions.OAIException;
|
||||
import org.xbib.oai.server.getrecord.GetRecordServerRequest;
|
||||
import org.xbib.oai.server.getrecord.GetRecordServerResponse;
|
||||
import org.xbib.oai.server.identify.IdentifyServerRequest;
|
||||
import org.xbib.oai.server.identify.IdentifyServerResponse;
|
||||
import org.xbib.oai.server.listidentifiers.ListIdentifiersServerRequest;
|
||||
import org.xbib.oai.server.listidentifiers.ListIdentifiersServerResponse;
|
||||
import org.xbib.oai.server.listmetadataformats.ListMetadataFormatsServerRequest;
|
||||
import org.xbib.oai.server.listmetadataformats.ListMetadataFormatsServerResponse;
|
||||
import org.xbib.oai.server.listrecords.ListRecordsServerRequest;
|
||||
import org.xbib.oai.server.listrecords.ListRecordsServerResponse;
|
||||
import org.xbib.oai.server.listsets.ListSetsServerRequest;
|
||||
import org.xbib.oai.server.listsets.ListSetsServerResponse;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAI server.
|
||||
*/
|
||||
public interface OAIServer {
|
||||
|
||||
URL getURL();
|
||||
|
||||
OAISession newSession() throws URISyntaxException;
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve information about a repository.
|
||||
* Some of the information returned is required as part of the OAI-PMH.
|
||||
* Repositories may also employ the Identify verb to return additional
|
||||
* descriptive information.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void identify(IdentifyServerRequest request, IdentifyServerResponse response) throws OAIException;
|
||||
|
||||
/**
|
||||
* This verb is an abbreviated form of ListRecords, retrieving only
|
||||
* headers rather than records. Optional arguments permit selective
|
||||
* harvesting of headers based on set membership and/or datestamp.
|
||||
* Depending on the repository's support for deletions, a returned
|
||||
* header may have a status attribute of "deleted" if a record
|
||||
* matching the arguments specified in the request has been deleted.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void listIdentifiers(ListIdentifiersServerRequest request, ListIdentifiersServerResponse response) throws OAIException;
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve the metadata formats available
|
||||
* from a repository. An optional argument restricts the request
|
||||
* to the formats available for a specific item.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void listMetadataFormats(ListMetadataFormatsServerRequest request, ListMetadataFormatsServerResponse response)
|
||||
throws OAIException;
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve the set structure of a repository,
|
||||
* useful for selective harvesting.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void listSets(ListSetsServerRequest request, ListSetsServerResponse response) throws OAIException;
|
||||
|
||||
/**
|
||||
* This verb is used to harvest records from a repository.
|
||||
* Optional arguments permit selective harvesting of records based on
|
||||
* set membership and/or datestamp. Depending on the repository's
|
||||
* support for deletions, a returned header may have a status
|
||||
* attribute of "deleted" if a record matching the arguments
|
||||
* specified in the request has been deleted. No metadata
|
||||
* will be present for records with deleted status.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void listRecords(ListRecordsServerRequest request, ListRecordsServerResponse response) throws OAIException;
|
||||
|
||||
/**
|
||||
* This verb is used to retrieve an individual metadata record from
|
||||
* a repository. Required arguments specify the identifier of the item
|
||||
* from which the record is requested and the format of the metadata
|
||||
* that should be included in the record. Depending on the level at
|
||||
* which a repository tracks deletions, a header with a "deleted" value
|
||||
* for the status attribute may be returned, in case the metadata format
|
||||
* specified by the metadataPrefix is no longer available from the
|
||||
* repository or from the specified item.
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @throws OAIException if verb fails
|
||||
*/
|
||||
void getRecord(GetRecordServerRequest request, GetRecordServerResponse response) throws OAIException;
|
||||
|
||||
Date getLastModified();
|
||||
|
||||
String getRepositoryName();
|
||||
|
||||
URL getBaseURL();
|
||||
|
||||
String getProtocolVersion();
|
||||
|
||||
String getAdminEmail();
|
||||
|
||||
String getEarliestDatestamp();
|
||||
|
||||
String getDeletedRecord();
|
||||
|
||||
String getGranularity();
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.xbib.oai.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class OAIServiceFactory {
|
||||
|
||||
private static final Map<URL, OAIServer> services = new HashMap<>();
|
||||
|
||||
private static final OAIServiceFactory instance = new OAIServiceFactory();
|
||||
|
||||
private OAIServiceFactory() {
|
||||
ServiceLoader<OAIServer> loader = ServiceLoader.load(OAIServer.class);
|
||||
for (OAIServer service : loader) {
|
||||
if (!services.containsKey(service.getURL())) {
|
||||
services.put(service.getURL(), service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static OAIServiceFactory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static OAIServer getDefaultService() {
|
||||
return services.isEmpty() ? null : services.entrySet().iterator().next().getValue();
|
||||
}
|
||||
|
||||
public static OAIServer getService(URL url) {
|
||||
if (services.containsKey(url)) {
|
||||
return services.get(url);
|
||||
}
|
||||
throw new IllegalArgumentException("OAI service " + url + " not found in " + services);
|
||||
}
|
||||
|
||||
public static OAIServer getService(String name) {
|
||||
Properties properties = new Properties();
|
||||
InputStream in = instance.getClass().getResourceAsStream("/org/xbib/oai/service/" + name + ".properties");
|
||||
if (in != null) {
|
||||
try {
|
||||
properties.load(in);
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("service " + name + " not found");
|
||||
}
|
||||
return new PropertiesOAIServer(properties);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package org.xbib.oai.server;
|
||||
|
||||
import org.xbib.oai.OAISession;
|
||||
import org.xbib.oai.exceptions.OAIException;
|
||||
import org.xbib.oai.server.getrecord.GetRecordServerRequest;
|
||||
import org.xbib.oai.server.getrecord.GetRecordServerResponse;
|
||||
import org.xbib.oai.server.identify.IdentifyServerRequest;
|
||||
import org.xbib.oai.server.identify.IdentifyServerResponse;
|
||||
import org.xbib.oai.server.listidentifiers.ListIdentifiersServerRequest;
|
||||
import org.xbib.oai.server.listidentifiers.ListIdentifiersServerResponse;
|
||||
import org.xbib.oai.server.listmetadataformats.ListMetadataFormatsServerRequest;
|
||||
import org.xbib.oai.server.listmetadataformats.ListMetadataFormatsServerResponse;
|
||||
import org.xbib.oai.server.listrecords.ListRecordsServerRequest;
|
||||
import org.xbib.oai.server.listrecords.ListRecordsServerResponse;
|
||||
import org.xbib.oai.server.listsets.ListSetsServerRequest;
|
||||
import org.xbib.oai.server.listsets.ListSetsServerResponse;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PropertiesOAIServer implements OAIServer {
|
||||
|
||||
public static final String ADAPTER_URI = "uri";
|
||||
|
||||
public static final String STYLESHEET = "stylesheet";
|
||||
|
||||
public static final String REPOSITORY_NAME = "identify.repositoryName";
|
||||
|
||||
public static final String BASE_URL = "identify.baseURL";
|
||||
|
||||
public static final String PROTOCOL_VERSION = "identify.protocolVersion";
|
||||
|
||||
public static final String ADMIN_EMAIL = "identify.adminEmail";
|
||||
|
||||
public static final String EARLIEST_DATESTAMP = "identify.earliestDatestamp";
|
||||
|
||||
public static final String DELETED_RECORD = "identify.deletedRecord";
|
||||
|
||||
public static final String GRANULARITY = "identify.granularity";
|
||||
|
||||
private Properties properties;
|
||||
|
||||
public PropertiesOAIServer(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
try {
|
||||
return new URL(properties.getProperty(ADAPTER_URI).trim());
|
||||
} catch (MalformedURLException e) {
|
||||
//
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getStylesheet() {
|
||||
return properties.getProperty(STYLESHEET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryName() {
|
||||
return properties.getProperty(REPOSITORY_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getBaseURL() {
|
||||
try {
|
||||
return new URL(properties.getProperty(BASE_URL));
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocolVersion() {
|
||||
return properties.getProperty(PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdminEmail() {
|
||||
return properties.getProperty(ADMIN_EMAIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEarliestDatestamp() {
|
||||
return properties.getProperty(EARLIEST_DATESTAMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeletedRecord() {
|
||||
return properties.getProperty(DELETED_RECORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGranularity() {
|
||||
return properties.getProperty(GRANULARITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAISession newSession() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModified() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void identify(IdentifyServerRequest request, IdentifyServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listMetadataFormats(ListMetadataFormatsServerRequest request, ListMetadataFormatsServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listSets(ListSetsServerRequest request, ListSetsServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listIdentifiers(ListIdentifiersServerRequest request, ListIdentifiersServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listRecords(ListRecordsServerRequest request, ListRecordsServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRecord(GetRecordServerRequest request, GetRecordServerResponse response)
|
||||
throws OAIException {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package org.xbib.oai.server;
|
||||
|
||||
import org.xbib.oai.OAIConstants;
|
||||
import org.xbib.oai.OAIRequest;
|
||||
import org.xbib.oai.util.ResumptionToken;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract class ServerOAIRequest implements OAIRequest {
|
||||
|
||||
private String path;
|
||||
|
||||
private Map<String, String> parameters;
|
||||
|
||||
private ResumptionToken<?> token;
|
||||
|
||||
private String set;
|
||||
|
||||
private String metadataPrefix;
|
||||
|
||||
private Instant from;
|
||||
|
||||
private Instant until;
|
||||
|
||||
private boolean retry;
|
||||
|
||||
protected ServerOAIRequest() {
|
||||
this.parameters = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSet(String set) {
|
||||
this.set = set;
|
||||
parameters.put(OAIConstants.SET_PARAMETER, set);
|
||||
}
|
||||
|
||||
public String getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadataPrefix(String prefix) {
|
||||
this.metadataPrefix = prefix;
|
||||
parameters.put(OAIConstants.METADATA_PREFIX_PARAMETER, prefix);
|
||||
}
|
||||
|
||||
public String getMetadataPrefix() {
|
||||
return metadataPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrom(Instant from) {
|
||||
this.from = from;
|
||||
parameters.put(OAIConstants.FROM_PARAMETER, from.toString());
|
||||
}
|
||||
|
||||
public Instant getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUntil(Instant until) {
|
||||
this.until = until;
|
||||
parameters.put(OAIConstants.UNTIL_PARAMETER, until.toString());
|
||||
}
|
||||
|
||||
public Instant getUntil() {
|
||||
return until;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResumptionToken(ResumptionToken<?> token) {
|
||||
this.token = token;
|
||||
if (token != null) {
|
||||
parameters.put(OAIConstants.RESUMPTION_TOKEN_PARAMETER, token.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResumptionToken<?> getResumptionToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setRetry(boolean retry) {
|
||||
this.retry = retry;
|
||||
}
|
||||
|
||||
public boolean isRetry() {
|
||||
return retry;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Map<String, String> getParameterMap() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.xbib.oai.server;
|
||||
|
||||
import org.xbib.oai.OAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import javax.xml.stream.util.XMLEventConsumer;
|
||||
|
||||
/**
|
||||
* Default OAI response.
|
||||
*/
|
||||
public class ServerOAIResponse implements OAIResponse {
|
||||
|
||||
private String format;
|
||||
|
||||
private XMLEventConsumer consumer;
|
||||
|
||||
public String getOutputFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
}
|
||||
|
||||
|
||||
public ServerOAIResponse setConsumer(XMLEventConsumer consumer) {
|
||||
this.consumer = consumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLEventConsumer getConsumer() {
|
||||
return consumer;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.getrecord;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GetRecordServerRequest extends ServerOAIRequest {
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.oai.server.getrecord;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GetRecordServerResponse extends ServerOAIResponse {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI get record.
|
||||
*/
|
||||
package org.xbib.oai.server.getrecord;
|
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.oai.server.identify;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IdentifyServerRequest extends ServerOAIRequest {
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.xbib.oai.server.identify;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IdentifyServerResponse extends ServerOAIResponse {
|
||||
|
||||
private String repositoryName;
|
||||
|
||||
private URL baseURL;
|
||||
|
||||
private String protocolVersion;
|
||||
|
||||
private List<String> adminEmails = new ArrayList<>();
|
||||
|
||||
private Date earliestDatestamp;
|
||||
|
||||
private String deletedRecord;
|
||||
|
||||
private String granularity;
|
||||
|
||||
private String compression;
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
}
|
||||
|
||||
public void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
public String getRepositoryName() {
|
||||
return repositoryName;
|
||||
}
|
||||
|
||||
public void setBaseURL(URL url) {
|
||||
this.baseURL = url;
|
||||
}
|
||||
|
||||
public URL getBaseURL() {
|
||||
return baseURL;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(String protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public String getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void addAdminEmail(String email) {
|
||||
adminEmails.add(email);
|
||||
}
|
||||
|
||||
public List<String> getAdminEmails() {
|
||||
return adminEmails;
|
||||
}
|
||||
|
||||
public void setEarliestDatestamp(Date earliestDatestamp) {
|
||||
this.earliestDatestamp = earliestDatestamp;
|
||||
}
|
||||
|
||||
public Date getEarliestDatestamp() {
|
||||
return earliestDatestamp;
|
||||
}
|
||||
|
||||
public void setDeletedRecord(String deletedRecord) {
|
||||
this.deletedRecord = deletedRecord;
|
||||
}
|
||||
|
||||
public String getDeleteRecord() {
|
||||
return deletedRecord;
|
||||
}
|
||||
|
||||
public void setGranularity(String granularity) {
|
||||
this.granularity = granularity;
|
||||
}
|
||||
|
||||
public String getGranularity() {
|
||||
return granularity;
|
||||
}
|
||||
|
||||
public void setCompression(String compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public String getCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI identify verb.
|
||||
*/
|
||||
package org.xbib.oai.server.identify;
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listidentifiers;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListIdentifiersServerRequest extends ServerOAIRequest {
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listidentifiers;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListIdentifiersServerResponse extends ServerOAIResponse {
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list identifiers verb.
|
||||
*/
|
||||
package org.xbib.oai.server.listidentifiers;
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listmetadataformats;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListMetadataFormatsServerRequest extends ServerOAIRequest {
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listmetadataformats;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListMetadataFormatsServerResponse extends ServerOAIResponse {
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list metadata formats verb.
|
||||
*/
|
||||
package org.xbib.oai.server.listmetadataformats;
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listrecords;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListRecordsServerRequest extends ServerOAIRequest {
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.xbib.oai.server.listrecords;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListRecordsServerResponse extends ServerOAIResponse {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ListRecordsServerResponse.class.getName());
|
||||
|
||||
private String error;
|
||||
|
||||
private Date date;
|
||||
|
||||
private long expire;
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setExpire(long expire) {
|
||||
this.expire = expire;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(Writer writer) throws IOException {
|
||||
try {
|
||||
if (this.expire > 0L) {
|
||||
logger.log(Level.INFO, "waiting for {} seconds (retry-after)", expire);
|
||||
Thread.sleep(1000 * expire);
|
||||
this.expire = 0L;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.log(Level.WARNING, "interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list records verb.
|
||||
*/
|
||||
package org.xbib.oai.server.listrecords;
|
|
@ -0,0 +1,10 @@
|
|||
package org.xbib.oai.server.listsets;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListSetsServerRequest extends ServerOAIRequest {
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.oai.server.listsets;
|
||||
|
||||
import org.xbib.oai.server.ServerOAIResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListSetsServerResponse extends ServerOAIResponse {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* OAI list sets verb.
|
||||
*/
|
||||
package org.xbib.oai.server.listsets;
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for OAI server.
|
||||
*/
|
||||
package org.xbib.oai.server;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue