Add 'dspace-solr/' from commit 'bd02640bc5d871bd9da6bc73ff70a44ebbc659cf'

git-subtree-dir: dspace-solr
git-subtree-mainline: 53716c0f29
git-subtree-split: bd02640bc5
This commit is contained in:
KevinVdV
2013-08-05 12:20:40 +02:00
13 changed files with 1660 additions and 0 deletions

14
dspace-solr/.gitattributes vendored Normal file
View File

@@ -0,0 +1,14 @@
# Auto detect text files and perform LF normalization
* text=auto
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

1
dspace-solr/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
target/

1
dspace-solr/.travis.yml Normal file
View File

@@ -0,0 +1 @@
language: java

40
dspace-solr/LICENSE Normal file
View File

@@ -0,0 +1,40 @@
DSpace source code license:
Copyright (c) 2002-2012, DuraSpace. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name DuraSpace nor the name of the DSpace Foundation
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
DSpace uses third-party libraries which may be distributed under
different licenses to the above. Information about these licenses
is detailed in the LICENSES_THIRD_PARTY file at the root of the source
tree. You must agree to the terms of these licenses, in addition to
the above DSpace source code license, in order to use this software.

View File

@@ -0,0 +1,5 @@
The contents of this file are subject to the license and copyright
detailed in the LICENSE and NOTICE files at the root of the source
tree and available online at
http://www.dspace.org/license/

15
dspace-solr/NOTICE Normal file
View File

@@ -0,0 +1,15 @@
Licensing Notice
Fedora Commons joined with the DSpace Foundation and began operating under
the new name DuraSpace in July 2009. DuraSpace holds the copyrights of
the DSpace Foundation, Inc.
The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007
with a mission to promote and advance the dspace platform enabling management,
access and preservation of digital works. The Foundation was able to transfer
the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many
of the files in the source code may contain a copyright statement stating HP
and MIT possess the copyright, in these instances please note that the copy
right has transferred to the DSpace foundation, and subsequently to DuraSpace.

14
dspace-solr/README Normal file
View File

@@ -0,0 +1,14 @@
This is a special build of Apache Solr for DSpace (http://www.dspace.org).
This build is just a Maven WAR Overlay of the default Apache Solr Web application.
This Overlay adds the following DSpace customizations:
* A new 'org.dspace.solr.filters.LocalHostRestrictionFilter' class (which restricts Solr access to localhost)
* Customizes the default web.xml to utilize this new LocalHostRestrictionFilter
For more information about Maven WAR Overlays see:
http://maven.apache.org/plugins/maven-war-plugin/overlays.html
This project has been created under an alternate groupId to
be supported within the DSpace community and distributed
within the the 'org.dspace' groupId.

252
dspace-solr/pom.xml Normal file
View File

@@ -0,0 +1,252 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed
with this work for additional information regarding copyright
ownership. The ASF licenses this file to you under the Apache
License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-solr-parent</artifactId>
<version>3.5.0.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Apache Solr Parent POM</name>
<description>Apache Solr Parent POM</description>
<url>http://lucene.apache.org/solr</url>
<organization>
<name>DuraSpace</name>
<url>http://www.dspace.org</url>
</organization>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
<relativePath></relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 'root.basedir' is the path to the root [dspace-src] dir. It must be redefined by each child POM,
as it is used to reference the LICENSE_HEADER and *.properties file(s) in that directory. -->
<root.basedir>${basedir}</root.basedir>
</properties>
<inceptionYear>2006</inceptionYear>
<licenses>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
<license>
<name>DuraSpace BSD License</name>
<url>https://raw.github.com/DSpace/DSpace/master/LICENSE</url>
<distribution>repo</distribution>
<comments>
A BSD 3-Clause license for the DSpace codebase.
</comments>
</license>
</licenses>
<issueManagement>
<system>JIRA</system>
<url>https://jira.duraspace.org/browse/DS</url>
</issueManagement>
<scm>
<connection>scm:git:git@github.com:DSpace/dspace-solr.git</connection>
<developerConnection>scm:git:git@github.com:DSpace/dspace-solr.git</developerConnection>
<url>git@github.com:DSpace/dspace-solr.git</url>
</scm>
<mailingLists>
<mailingList>
<name>DSpace Technical Users List</name>
<subscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-tech
</subscribe>
<unsubscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-tech
</unsubscribe>
<post>dspace-tech AT lists.sourceforge.net</post>
<archive>
http://sourceforge.net/mailarchive/forum.php?forum_name=dspace-tech
</archive>
</mailingList>
<mailingList>
<name>DSpace Developers List</name>
<subscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-devel
</subscribe>
<unsubscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-devel
</unsubscribe>
<post>dspace-devel AT lists.sourceforge.net</post>
<archive>
http://sourceforge.net/mailarchive/forum.php?forum_name=dspace-devel
</archive>
</mailingList>
<mailingList>
<name>DSpace General Issues List</name>
<subscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-general
</subscribe>
<unsubscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-general
</unsubscribe>
<post>dspace-general AT lists.sourceforge.net</post>
<archive>
http://sourceforge.net/mailarchive/forum.php?forum_name=dspace-general
</archive>
</mailingList>
<mailingList>
<name>DSpace SCM Commit Change-Log</name>
<subscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-changelog
</subscribe>
<unsubscribe>
http://lists.sourceforge.net/mailman/listinfo/dspace-changelog
</unsubscribe>
<post>noreply AT lists.sourceforge.net</post>
<archive>
http://sourceforge.net/mailarchive/forum.php?forum_name=dspace-changelog
</archive>
</mailingList>
</mailingLists>
<developers>
<!-- If you developed this, please fill in -->
</developers>
<contributors>
<contributor>
<name>Add Your Name Here and submit a patch!</name>
<email>contributor at myu.edu</email>
<url>http://www.myu.edu/me</url>
<organization>My University</organization>
<organizationUrl>http://www.myu.edu</organizationUrl>
<roles>
<role>developer</role>
</roles>
<timezone>0</timezone>
</contributor>
</contributors>
<modules>
<module>webapp</module>
</modules>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.9.0</version>
<configuration>
<header>${root.basedir}/LICENSE_HEADER</header>
<excludes>
<exclude>src/main/*/org/apache/**</exclude>
<exclude>**/web.xml</exclude>
<exclude>**/src/test/resources/**</exclude>
<exclude>**/META-INF/**</exclude>
<exclude>**/robots.txt</exclude>
<exclude>**/*.LICENSE</exclude>
<exclude>**/LICENSE*</exclude>
<exclude>**/README*</exclude>
<exclude>**/NOTICE</exclude>
<exclude>**/readme*</exclude>
<exclude>**/.gitignore</exclude>
</excludes>
<mapping>
<!-- Custom DSpace file extensions which are not recognized by maven-release-plugin:
*.xmap, *.xslt, *.LICENSE -->
<xmap>XML_STYLE</xmap>
<xslt>XML_STYLE</xslt>
<LICENSE>TEXT</LICENSE>
</mapping>
<properties />
<encoding>UTF-8</encoding>
<strictCheck>true</strictCheck>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

147
dspace-solr/webapp/pom.xml Normal file
View File

@@ -0,0 +1,147 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed
with this work for additional information regarding copyright
ownership. The ASF licenses this file to you under the Apache
License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-solr-parent</artifactId>
<version>3.5.0.2-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-solr</artifactId>
<name>Apache Solr Webapp</name>
<description>Apache Solr Server</description>
<packaging>war</packaging>
<properties>
<!-- 'root.basedir' is the path to the root [dspace-src] dir. It must be redefined by each child POM,
as it is used to reference the LICENSE_HEADER and *.properties file(s) in that directory. -->
<root.basedir>${basedir}/..</root.basedir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<!-- This 'dspace-solr' WAR overlays the Apache Solr Web Application
available in Maven Central -->
<overlays>
<overlay>
<groupId>org.apache.solr</groupId>
<artifactId>solr</artifactId>
<!--
Exclude the solr core named apache-solr-core, this is needed because the dspace-solr changes
need to take precendence over the solr-core, the solr-core will still be loaded in the solr-core.jar
-->
<excludes>
<exclude>WEB-INF/lib/apache-solr-core-3.5.0.jar</exclude>
</excludes>
</overlay>
</overlays>
</configuration>
<executions>
<execution>
<!-- This execution creates a normal WAR (with all JARs, etc)-->
<id>webapp</id>
<configuration>
<primaryArtifact>true</primaryArtifact>
<archiveClasses>true</archiveClasses>
<attachClasses>true</attachClasses>
<classesClassifier>classes</classesClassifier>
<warSourceExcludes>WEB-INF/classes/**</warSourceExcludes>
<packagingExcludes>
WEB-INF/classes/**,
WEB-INF/lib/slf4j-jdk14-*.jar,
WEB-INF/lib/log4j-over-slf4j-*.jar
</packagingExcludes>
</configuration>
<goals>
<goal>war</goal>
</goals>
<phase>package</phase>
</execution>
<execution>
<!-- This execution creates a "skinny" WAR (without any JARs included)-->
<id>skinny</id>
<configuration>
<primaryArtifact>false</primaryArtifact>
<classifier>skinny</classifier>
<warSourceExcludes>WEB-INF/lib/**,WEB-INF/classes/**</warSourceExcludes>
<packagingExcludes>WEB-INF/lib/**,WEB-INF/classes/**</packagingExcludes>
</configuration>
<goals>
<goal>war</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Depends on the Apache 'solr' web application (see Overlay settings above)-->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr</artifactId>
<version>3.5.0</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-core</artifactId>
<version>3.5.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>3.5.0</version>
</dependency>
<!-- Replace J.U.L. logging with log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
<type>jar</type>
<version>1.2.16</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,839 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.handler.component;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.util.OpenBitSet;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.FacetParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.request.SimpleFacets;
import org.apache.solr.schema.FieldType;
import org.apache.solr.search.QueryParsing;
import java.io.IOException;
import java.net.URL;
import java.util.*;
/**
* TODO!
*
* @version $Id: FacetComponent.java 1152531 2011-07-31 00:43:33Z koji $
* @since solr 1.3
*/
public class FacetComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "facet";
@Override
public void prepare(ResponseBuilder rb) throws IOException
{
if (rb.req.getParams().getBool(FacetParams.FACET,false)) {
rb.setNeedDocSet( true );
rb.doFacets = true;
}
}
/**
* Actually run the query
* @param rb
*/
@Override
public void process(ResponseBuilder rb) throws IOException
{
if (rb.doFacets) {
SolrParams params = rb.req.getParams();
SimpleFacets f = new SimpleFacets(rb.req,
rb.getResults().docSet,
params,
rb );
// TODO ???? add this directly to the response, or to the builder?
rb.rsp.add( "facet_counts", f.getFacetCounts() );
}
}
private static final String commandPrefix = "{!" + CommonParams.TERMS + "=$";
@Override
public int distributedProcess(ResponseBuilder rb) throws IOException {
if (!rb.doFacets) {
return ResponseBuilder.STAGE_DONE;
}
if (rb.stage == ResponseBuilder.STAGE_GET_FIELDS) {
// overlap facet refinement requests (those shards that we need a count for
// particular facet values from), where possible, with
// the requests to get fields (because we know that is the
// only other required phase).
// We do this in distributedProcess so we can look at all of the
// requests in the outgoing queue at once.
for (int shardNum=0; shardNum<rb.shards.length; shardNum++) {
List<String> refinements = null;
for (DistribFieldFacet dff : rb._facetInfo.facets.values()) {
if (!dff.needRefinements) continue;
List<String> refList = dff._toRefine[shardNum];
if (refList == null || refList.size()==0) continue;
String key = dff.getKey(); // reuse the same key that was used for the main facet
String termsKey = key + "__terms";
String termsVal = StrUtils.join(refList, ',');
String facetCommand;
// add terms into the original facet.field command
// do it via parameter reference to avoid another layer of encoding.
String termsKeyEncoded = QueryParsing.encodeLocalParamVal(termsKey);
if (dff.localParams != null) {
facetCommand = commandPrefix+termsKeyEncoded + " " + dff.facetStr.substring(2);
} else {
facetCommand = commandPrefix+termsKeyEncoded+'}'+dff.field;
}
if (refinements == null) {
refinements = new ArrayList<String>();
}
refinements.add(facetCommand);
refinements.add(termsKey);
refinements.add(termsVal);
}
if (refinements == null) continue;
String shard = rb.shards[shardNum];
ShardRequest refine = null;
boolean newRequest = false;
// try to find a request that is already going out to that shard.
// If nshards becomes to great, we way want to move to hashing for better
// scalability.
for (ShardRequest sreq : rb.outgoing) {
if ((sreq.purpose & ShardRequest.PURPOSE_GET_FIELDS)!=0
&& sreq.shards != null
&& sreq.shards.length==1
&& sreq.shards[0].equals(shard))
{
refine = sreq;
break;
}
}
if (refine == null) {
// we didn't find any other suitable requests going out to that shard, so
// create one ourselves.
newRequest = true;
refine = new ShardRequest();
refine.shards = new String[]{rb.shards[shardNum]};
refine.params = new ModifiableSolrParams(rb.req.getParams());
// don't request any documents
refine.params.remove(CommonParams.START);
refine.params.set(CommonParams.ROWS,"0");
}
refine.purpose |= ShardRequest.PURPOSE_REFINE_FACETS;
refine.params.set(FacetParams.FACET, "true");
refine.params.remove(FacetParams.FACET_FIELD);
refine.params.remove(FacetParams.FACET_QUERY);
for (int i=0; i<refinements.size();) {
String facetCommand=refinements.get(i++);
String termsKey=refinements.get(i++);
String termsVal=refinements.get(i++);
refine.params.add(FacetParams.FACET_FIELD, facetCommand);
refine.params.set(termsKey, termsVal);
}
if (newRequest) {
rb.addRequest(this, refine);
}
}
}
return ResponseBuilder.STAGE_DONE;
}
@Override
public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest sreq) {
if (!rb.doFacets) return;
if ((sreq.purpose & ShardRequest.PURPOSE_GET_TOP_IDS) != 0) {
sreq.purpose |= ShardRequest.PURPOSE_GET_FACETS;
FacetInfo fi = rb._facetInfo;
if (fi == null) {
rb._facetInfo = fi = new FacetInfo();
fi.parse(rb.req.getParams(), rb);
// should already be true...
// sreq.params.set(FacetParams.FACET, "true");
}
sreq.params.remove(FacetParams.FACET_MINCOUNT);
sreq.params.remove(FacetParams.FACET_OFFSET);
sreq.params.remove(FacetParams.FACET_LIMIT);
for (DistribFieldFacet dff : fi.facets.values()) {
String paramStart = "f." + dff.field + '.';
sreq.params.remove(paramStart + FacetParams.FACET_MINCOUNT);
sreq.params.remove(paramStart + FacetParams.FACET_OFFSET);
dff.initialLimit = dff.limit <= 0 ? dff.limit : dff.offset + dff.limit;
if (dff.sort.equals(FacetParams.FACET_SORT_COUNT)) {
if (dff.limit > 0) {
// set the initial limit higher to increase accuracy
dff.initialLimit = (int)(dff.initialLimit * 1.5) + 10;
dff.initialMincount = 0; // TODO: we could change this to 1, but would then need more refinement for small facet result sets?
} else {
// if limit==-1, then no need to artificially lower mincount to 0 if it's 1
dff.initialMincount = Math.min(dff.minCount, 1);
}
} else {
// we're sorting by index order.
// if minCount==0, we should always be able to get accurate results w/o over-requesting or refining
// if minCount==1, we should be able to get accurate results w/o over-requesting, but we'll need to refine
// if minCount==n (>1), we can set the initialMincount to minCount/nShards, rounded up.
// For example, we know that if minCount=10 and we have 3 shards, then at least one shard must have a count of 4 for the term
// For the minCount>1 case, we can generate too short of a list (miss terms at the end of the list) unless limit==-1
// For example: each shard could produce a list of top 10, but some of those could fail to make it into the combined list (i.e.
// we needed to go beyond the top 10 to generate the top 10 combined). Overrequesting can help a little here, but not as
// much as when sorting by count.
if (dff.minCount <= 1) {
dff.initialMincount = dff.minCount;
} else {
dff.initialMincount = (int)Math.ceil((double)dff.minCount / rb.shards.length);
// dff.initialMincount = 1;
}
}
if (dff.initialMincount != 0) {
sreq.params.set(paramStart + FacetParams.FACET_MINCOUNT, dff.initialMincount);
}
// Currently this is for testing only and allows overriding of the
// facet.limit set to the shards
dff.initialLimit = rb.req.getParams().getInt("facet.shard.limit", dff.initialLimit);
sreq.params.set(paramStart + FacetParams.FACET_LIMIT, dff.initialLimit);
}
} else {
// turn off faceting on other requests
sreq.params.set(FacetParams.FACET, "false");
// we could optionally remove faceting params
}
}
@Override
public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
if (!rb.doFacets) return;
if ((sreq.purpose & ShardRequest.PURPOSE_GET_FACETS)!=0) {
countFacets(rb, sreq);
} else if ((sreq.purpose & ShardRequest.PURPOSE_REFINE_FACETS)!=0) {
refineFacets(rb, sreq);
}
}
private void countFacets(ResponseBuilder rb, ShardRequest sreq) {
FacetInfo fi = rb._facetInfo;
for (ShardResponse srsp: sreq.responses) {
int shardNum = rb.getShardNum(srsp.getShard());
NamedList facet_counts = (NamedList)srsp.getSolrResponse().getResponse().get("facet_counts");
// handle facet queries
NamedList facet_queries = (NamedList)facet_counts.get("facet_queries");
if (facet_queries != null) {
for (int i=0; i<facet_queries.size(); i++) {
String returnedKey = facet_queries.getName(i);
long count = ((Number)facet_queries.getVal(i)).longValue();
QueryFacet qf = fi.queryFacets.get(returnedKey);
qf.count += count;
}
}
// step through each facet.field, adding results from this shard
NamedList facet_fields = (NamedList)facet_counts.get("facet_fields");
if (facet_fields != null) {
for (DistribFieldFacet dff : fi.facets.values()) {
dff.add(shardNum, (NamedList)facet_fields.get(dff.getKey()), dff.initialLimit);
}
}
// Distributed facet_dates
//
// The implementation below uses the first encountered shard's
// facet_dates as the basis for subsequent shards' data to be merged.
// (the "NOW" param should ensure consistency)
@SuppressWarnings("unchecked")
SimpleOrderedMap<SimpleOrderedMap<Object>> facet_dates =
(SimpleOrderedMap<SimpleOrderedMap<Object>>)
facet_counts.get("facet_dates");
if (facet_dates != null) {
// go through each facet_date
for (Map.Entry<String,SimpleOrderedMap<Object>> entry : facet_dates) {
final String field = entry.getKey();
if (fi.dateFacets.get(field) == null) {
// first time we've seen this field, no merging
fi.dateFacets.add(field, entry.getValue());
} else {
// not the first time, merge current field
SimpleOrderedMap<Object> shardFieldValues
= entry.getValue();
SimpleOrderedMap<Object> existFieldValues
= fi.dateFacets.get(field);
for (Map.Entry<String,Object> existPair : existFieldValues) {
final String key = existPair.getKey();
if (key.equals("gap") ||
key.equals("end") ||
key.equals("start")) {
// we can skip these, must all be the same across shards
continue;
}
// can be null if inconsistencies in shards responses
Integer newValue = (Integer) shardFieldValues.get(key);
if (null != newValue) {
Integer oldValue = ((Integer) existPair.getValue());
existPair.setValue(oldValue + newValue);
}
}
}
}
}
// Distributed facet_ranges
//
// The implementation below uses the first encountered shard's
// facet_ranges as the basis for subsequent shards' data to be merged.
@SuppressWarnings("unchecked")
SimpleOrderedMap<SimpleOrderedMap<Object>> facet_ranges =
(SimpleOrderedMap<SimpleOrderedMap<Object>>)
facet_counts.get("facet_ranges");
if (facet_ranges != null) {
// go through each facet_range
for (Map.Entry<String,SimpleOrderedMap<Object>> entry : facet_ranges) {
final String field = entry.getKey();
if (fi.rangeFacets.get(field) == null) {
// first time we've seen this field, no merging
fi.rangeFacets.add(field, entry.getValue());
} else {
// not the first time, merge current field counts
@SuppressWarnings("unchecked")
NamedList<Integer> shardFieldValues
= (NamedList<Integer>) entry.getValue().get("counts");
@SuppressWarnings("unchecked")
NamedList<Integer> existFieldValues
= (NamedList<Integer>) fi.rangeFacets.get(field).get("counts");
for (Map.Entry<String,Integer> existPair : existFieldValues) {
final String key = existPair.getKey();
// can be null if inconsistencies in shards responses
Integer newValue = shardFieldValues.get(key);
if (null != newValue) {
Integer oldValue = existPair.getValue();
existPair.setValue(oldValue + newValue);
}
}
}
}
}
}
//
// This code currently assumes that there will be only a single
// request ((with responses from all shards) sent out to get facets...
// otherwise we would need to wait until all facet responses were received.
//
for (DistribFieldFacet dff : fi.facets.values()) {
// no need to check these facets for refinement
if (dff.initialLimit <= 0 && dff.initialMincount == 0) continue;
// only other case where index-sort doesn't need refinement is if minCount==0
if (dff.minCount == 0 && dff.sort.equals(FacetParams.FACET_SORT_INDEX)) continue;
@SuppressWarnings("unchecked") // generic array's are annoying
List<String>[] tmp = (List<String>[]) new List[rb.shards.length];
dff._toRefine = tmp;
ShardFacetCount[] counts = dff.getCountSorted();
int ntop = Math.min(counts.length, dff.limit >= 0 ? dff.offset + dff.limit : Integer.MAX_VALUE);
long smallestCount = counts.length == 0 ? 0 : counts[ntop-1].count;
for (int i=0; i<counts.length; i++) {
ShardFacetCount sfc = counts[i];
boolean needRefinement = false;
if (i<ntop) {
// automatically flag the top values for refinement
// this should always be true for facet.sort=index
needRefinement = true;
} else {
// this logic should only be invoked for facet.sort=index (for now)
// calculate the maximum value that this term may have
// and if it is >= smallestCount, then flag for refinement
long maxCount = sfc.count;
for (int shardNum=0; shardNum<rb.shards.length; shardNum++) {
OpenBitSet obs = dff.counted[shardNum];
if (!obs.get(sfc.termNum)) {
// if missing from this shard, add the max it could be
maxCount += dff.maxPossible(sfc,shardNum);
}
}
if (maxCount >= smallestCount) {
// TODO: on a tie, we could check the term values
needRefinement = true;
}
}
if (needRefinement) {
// add a query for each shard missing the term that needs refinement
for (int shardNum=0; shardNum<rb.shards.length; shardNum++) {
OpenBitSet obs = dff.counted[shardNum];
if (!obs.get(sfc.termNum) && dff.maxPossible(sfc,shardNum)>0) {
dff.needRefinements = true;
List<String> lst = dff._toRefine[shardNum];
if (lst == null) {
lst = dff._toRefine[shardNum] = new ArrayList<String>();
}
lst.add(sfc.name);
}
}
}
}
}
}
private void refineFacets(ResponseBuilder rb, ShardRequest sreq) {
FacetInfo fi = rb._facetInfo;
for (ShardResponse srsp: sreq.responses) {
// int shardNum = rb.getShardNum(srsp.shard);
NamedList facet_counts = (NamedList)srsp.getSolrResponse().getResponse().get("facet_counts");
NamedList facet_fields = (NamedList)facet_counts.get("facet_fields");
if (facet_fields == null) continue; // this can happen when there's an exception
for (int i=0; i<facet_fields.size(); i++) {
String key = facet_fields.getName(i);
DistribFieldFacet dff = fi.facets.get(key);
if (dff == null) continue;
NamedList shardCounts = (NamedList)facet_fields.getVal(i);
for (int j=0; j<shardCounts.size(); j++) {
String name = shardCounts.getName(j);
long count = ((Number)shardCounts.getVal(j)).longValue();
ShardFacetCount sfc = dff.counts.get(name);
sfc.count += count;
}
}
}
}
@Override
public void finishStage(ResponseBuilder rb) {
if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) return;
// wait until STAGE_GET_FIELDS
// so that "result" is already stored in the response (for aesthetics)
FacetInfo fi = rb._facetInfo;
NamedList<Object> facet_counts = new SimpleOrderedMap<Object>();
NamedList facet_queries = new SimpleOrderedMap();
facet_counts.add("facet_queries",facet_queries);
for (QueryFacet qf : fi.queryFacets.values()) {
facet_queries.add(qf.getKey(), num(qf.count));
}
NamedList<Object> facet_fields = new SimpleOrderedMap<Object>();
facet_counts.add("facet_fields", facet_fields);
for (DistribFieldFacet dff : fi.facets.values()) {
NamedList<Object> fieldCounts = new NamedList<Object>(); // order is more important for facets
facet_fields.add(dff.getKey(), fieldCounts);
ShardFacetCount[] counts;
boolean countSorted = dff.sort.equals(FacetParams.FACET_SORT_COUNT);
if (countSorted) {
counts = dff.countSorted;
if (counts == null || dff.needRefinements) {
counts = dff.getCountSorted();
}
} else if (dff.sort.equals(FacetParams.FACET_SORT_INDEX)) {
counts = dff.getLexSorted();
} else { // TODO: log error or throw exception?
counts = dff.getLexSorted();
}
if (countSorted) {
int end = dff.limit < 0 ? counts.length : Math.min(dff.offset + dff.limit, counts.length);
for (int i=dff.offset; i<end; i++) {
if (counts[i].count < dff.minCount) {
break;
}
fieldCounts.add(counts[i].name, num(counts[i].count));
}
} else {
int off = dff.offset;
int lim = dff.limit >= 0 ? dff.limit : Integer.MAX_VALUE;
// index order...
for (int i=0; i<counts.length; i++) {
long count = counts[i].count;
if (count < dff.minCount) continue;
if (off > 0) {
off--;
continue;
}
if (lim <= 0) {
break;
}
lim--;
fieldCounts.add(counts[i].name, num(count));
}
}
if (dff.missing) {
fieldCounts.add(null, num(dff.missingCount));
}
}
facet_counts.add("facet_dates", fi.dateFacets);
facet_counts.add("facet_ranges", fi.rangeFacets);
rb.rsp.add("facet_counts", facet_counts);
rb._facetInfo = null; // could be big, so release asap
}
// use <int> tags for smaller facet counts (better back compatibility)
private Number num(long val) {
if (val < Integer.MAX_VALUE) return (int)val;
else return val;
}
private Number num(Long val) {
if (val.longValue() < Integer.MAX_VALUE) return val.intValue();
else return val;
}
/////////////////////////////////////////////
/// SolrInfoMBean
////////////////////////////////////////////
@Override
public String getDescription() {
return "Handle Faceting";
}
@Override
public String getVersion() {
return "$Revision: 1152531 $";
}
@Override
public String getSourceId() {
return "$Id: FacetComponent.java 1152531 2011-07-31 00:43:33Z koji $";
}
@Override
public String getSource() {
return "$URL: http://svn.apache.org/repos/asf/lucene/dev/tags/lucene_solr_3_5_0/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java $";
}
@Override
public URL[] getDocs() {
return null;
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class FacetInfo {
public LinkedHashMap<String,QueryFacet> queryFacets;
public LinkedHashMap<String,DistribFieldFacet> facets;
public SimpleOrderedMap<SimpleOrderedMap<Object>> dateFacets
= new SimpleOrderedMap<SimpleOrderedMap<Object>>();
public SimpleOrderedMap<SimpleOrderedMap<Object>> rangeFacets
= new SimpleOrderedMap<SimpleOrderedMap<Object>>();
public List exceptionList;
void parse(SolrParams params, ResponseBuilder rb) {
queryFacets = new LinkedHashMap<String,QueryFacet>();
facets = new LinkedHashMap<String,DistribFieldFacet>();
String[] facetQs = params.getParams(FacetParams.FACET_QUERY);
if (facetQs != null) {
for (String query : facetQs) {
QueryFacet queryFacet = new QueryFacet(rb, query);
queryFacets.put(queryFacet.getKey(), queryFacet);
}
}
String[] facetFs = params.getParams(FacetParams.FACET_FIELD);
if (facetFs != null) {
for (String field : facetFs) {
DistribFieldFacet ff = new DistribFieldFacet(rb, field);
facets.put(ff.getKey(), ff);
}
}
}
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class FacetBase {
String facetType; // facet.field, facet.query, etc (make enum?)
String facetStr; // original parameter value of facetStr
String facetOn; // the field or query, absent localParams if appropriate
private String key; // label in the response for the result... "foo" for {!key=foo}myfield
SolrParams localParams; // any local params for the facet
public FacetBase(ResponseBuilder rb, String facetType, String facetStr) {
this.facetType = facetType;
this.facetStr = facetStr;
try {
this.localParams = QueryParsing.getLocalParams(facetStr, rb.req.getParams());
} catch (ParseException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
}
this.facetOn = facetStr;
this.key = facetStr;
if (localParams != null) {
// remove local params unless it's a query
if (!facetType.equals(FacetParams.FACET_QUERY)) {
facetOn = localParams.get(CommonParams.VALUE);
key = facetOn;
}
key = localParams.get(CommonParams.OUTPUT_KEY, key);
}
}
/** returns the key in the response that this facet will be under */
public String getKey() { return key; }
public String getType() { return facetType; }
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class QueryFacet extends FacetBase {
public long count;
public QueryFacet(ResponseBuilder rb, String facetStr) {
super(rb, FacetParams.FACET_QUERY, facetStr);
}
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class FieldFacet extends FacetBase {
public String field; // the field to facet on... "myfield" for {!key=foo}myfield
public FieldType ftype;
public int offset;
public int limit;
public int minCount;
public String sort;
public boolean missing;
public String prefix;
public long missingCount;
public FieldFacet(ResponseBuilder rb, String facetStr) {
super(rb, FacetParams.FACET_FIELD, facetStr);
fillParams(rb, rb.req.getParams(), facetOn);
}
private void fillParams(ResponseBuilder rb, SolrParams params, String field) {
this.field = field;
this.ftype = rb.req.getSchema().getFieldTypeNoEx(this.field);
this.offset = params.getFieldInt(field, FacetParams.FACET_OFFSET, 0);
this.limit = params.getFieldInt(field, FacetParams.FACET_LIMIT, 100);
Integer mincount = params.getFieldInt(field, FacetParams.FACET_MINCOUNT);
if (mincount==null) {
Boolean zeros = params.getFieldBool(field, FacetParams.FACET_ZEROS);
// mincount = (zeros!=null && zeros) ? 0 : 1;
mincount = (zeros!=null && !zeros) ? 1 : 0;
// current default is to include zeros.
}
this.minCount = mincount;
this.missing = params.getFieldBool(field, FacetParams.FACET_MISSING, false);
// default to sorting by count if there is a limit.
this.sort = params.getFieldParam(field, FacetParams.FACET_SORT, limit>0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX);
if (this.sort.equals(FacetParams.FACET_SORT_COUNT_LEGACY)) {
this.sort = FacetParams.FACET_SORT_COUNT;
} else if (this.sort.equals(FacetParams.FACET_SORT_INDEX_LEGACY)) {
this.sort = FacetParams.FACET_SORT_INDEX;
}
this.prefix = params.getFieldParam(field,FacetParams.FACET_PREFIX);
}
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class DistribFieldFacet extends FieldFacet {
public List<String>[] _toRefine; // a List<String> of refinements needed, one for each shard.
// SchemaField sf; // currently unneeded
// the max possible count for a term appearing on no list
public long missingMaxPossible;
// the max possible count for a missing term for each shard (indexed by shardNum)
public long[] missingMax;
public OpenBitSet[] counted; // a bitset for each shard, keeping track of which terms seen
public HashMap<String,ShardFacetCount> counts = new HashMap<String,ShardFacetCount>(128);
public int termNum;
public int initialLimit; // how many terms requested in first phase
public int initialMincount; // mincount param sent to each shard
public boolean needRefinements;
public ShardFacetCount[] countSorted;
DistribFieldFacet(ResponseBuilder rb, String facetStr) {
super(rb, facetStr);
// sf = rb.req.getSchema().getField(field);
missingMax = new long[rb.shards.length];
counted = new OpenBitSet[rb.shards.length];
}
void add(int shardNum, NamedList shardCounts, int numRequested) {
// shardCounts could be null if there was an exception
int sz = shardCounts == null ? 0 : shardCounts.size();
int numReceived = sz;
OpenBitSet terms = new OpenBitSet(termNum+sz);
long last = 0;
for (int i=0; i<sz; i++) {
String name = shardCounts.getName(i);
long count = ((Number)shardCounts.getVal(i)).longValue();
if (name == null) {
missingCount += count;
numReceived--;
} else {
ShardFacetCount sfc = counts.get(name);
if (sfc == null) {
sfc = new ShardFacetCount();
sfc.name = name;
sfc.indexed = ftype == null ? sfc.name : ftype.toInternal(sfc.name);
sfc.termNum = termNum++;
counts.put(name, sfc);
}
sfc.count += count;
terms.fastSet(sfc.termNum);
last = count;
}
}
// the largest possible missing term is initialMincount if we received less
// than the number requested.
if (numRequested<0 || numRequested != 0 && numReceived < numRequested) {
last = initialMincount;
}
missingMaxPossible += last;
missingMax[shardNum] = last;
counted[shardNum] = terms;
}
public ShardFacetCount[] getLexSorted() {
ShardFacetCount[] arr = counts.values().toArray(new ShardFacetCount[counts.size()]);
Arrays.sort(arr, new Comparator<ShardFacetCount>() {
public int compare(ShardFacetCount o1, ShardFacetCount o2) {
return o1.indexed.compareTo(o2.indexed);
}
});
countSorted = arr;
return arr;
}
public ShardFacetCount[] getCountSorted() {
ShardFacetCount[] arr = counts.values().toArray(new ShardFacetCount[counts.size()]);
Arrays.sort(arr, new Comparator<ShardFacetCount>() {
public int compare(ShardFacetCount o1, ShardFacetCount o2) {
if (o2.count < o1.count) return -1;
else if (o1.count < o2.count) return 1;
return o1.indexed.compareTo(o2.indexed);
}
});
countSorted = arr;
return arr;
}
// returns the max possible value this ShardFacetCount could have for this shard
// (assumes the shard did not report a count for this value)
long maxPossible(ShardFacetCount sfc, int shardNum) {
return missingMax[shardNum];
// TODO: could store the last term in the shard to tell if this term
// comes before or after it. If it comes before, we could subtract 1
}
}
/**
* <b>This API is experimental and subject to change</b>
*/
public static class ShardFacetCount {
public String name;
public String indexed; // the indexed form of the name... used for comparisons.
public long count;
public int termNum; // term number starting at 0 (used in bit arrays)
@Override
public String toString() {
return "{term="+name+",termNum="+termNum+",count="+count+"}";
}
}
}

View File

@@ -0,0 +1,86 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.solr.filters;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Level;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.helpers.Loader;
import org.apache.log4j.spi.Configurator;
import org.apache.log4j.spi.RootLogger;
import org.apache.log4j.xml.DOMConfigurator;
/**
* Initialize Log4J at application startup.
* This class mimics the default Log4J initialization procedure, except
* that it is controlled by context parameters rather than system properties.
*
* @author Mark H. Wood
*/
public class ConfigureLog4jListener
implements ServletContextListener
{
public void contextInitialized(ServletContextEvent sce)
{
ServletContext ctx = sce.getServletContext();
String logConfig = ctx.getInitParameter("log4j.configuration");
if (null == logConfig)
logConfig = "log4j.properties";
URL configURL;
try {
configURL = new URL(logConfig);
} catch (MalformedURLException e) {
configURL = Loader.getResource(logConfig);
}
if (null == configURL)
{
ctx.log("Log4J configuration not found. Left unconfigured.");
return;
}
else
{
ctx.log(" In context " + ctx.getContextPath() +
", configuring Log4J from " + configURL.toExternalForm());
String configuratorName = ctx.getInitParameter("log4j.configuratorClass");
if (null != configuratorName)
{
Configurator configurator;
try
{
configurator = (Configurator) Class.forName(configuratorName).newInstance();
} catch (Exception ex)
{
ctx.log("Unable to load custom Log4J configuration class '"
+ configuratorName + "': " + ex.getMessage());
return;
}
configurator.doConfigure(configURL, new Hierarchy(new RootLogger(Level.OFF)));
}
else if (configURL.getFile().endsWith(".xml"))
DOMConfigurator.configure(configURL);
else
PropertyConfigurator.configure(configURL);
}
}
public void contextDestroyed(ServletContextEvent sce)
{
// Nothing to be done
}
}

View File

@@ -0,0 +1,65 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.solr.filters;
import java.io.IOException;
import java.net.InetAddress;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class LocalHostRestrictionFilter implements Filter {
private boolean enabled = true;
public LocalHostRestrictionFilter() {
// TODO Auto-generated constructor stub
}
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(enabled){
InetAddress ia = InetAddress.getLocalHost();
String localAddr = ia.getHostAddress();
String remoteAddr = request.getRemoteAddr();
if(!(localAddr.equals(remoteAddr) || remoteAddr.equals("127.0.0.1") || remoteAddr.startsWith("0:0:0:0:0:0:0:1")))
{
((HttpServletResponse)response).sendError(403);
return;
}
}
chain.doFilter(request, response);
}
/**
*
*/
public void init(FilterConfig arg0) throws ServletException {
String restrict = arg0.getServletContext().getInitParameter("LocalHostRestrictionFilter.localhost");
if("false".equalsIgnoreCase(restrict))
{
enabled = false;
}
}
}

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<web-app>
<!-- Uncomment if you are trying to use a Resin version before 3.0.19.
Their XML implementation isn't entirely compatible with Xerces.
Below are the implementations to use with Sun's JVM.
<system-property javax.xml.xpath.XPathFactory=
"com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl"/>
<system-property javax.xml.parsers.DocumentBuilderFactory=
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"/>
<system-property javax.xml.parsers.SAXParserFactory=
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"/>
-->
<!-- People who want to hardcode their "Solr Home" directly into the
WAR File can set the JNDI property here...
-->
<!--
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>/put/your/solr/home/here</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
-->
<context-param>
<param-name>log4j.configuration</param-name>
<param-value>file://${dspace.dir}/config/log4j.properties</param-value>
<description>URL locating a Log4J configuration file (properties or XML).</description>
</context-param>
<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
<filter>
<filter-name>LocalHostRestrictionFilter</filter-name>
<filter-class>org.dspace.solr.filters.LocalHostRestrictionFilter</filter-class>
</filter>
<!--
Any path (name) registered in solrconfig.xml will be sent to that
filter
-->
<filter>
<filter-name>SolrRequestFilter</filter-name>
<filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>
<!-- If you are wiring Solr into a larger web application which controls
the web context root, you will probably want to mount Solr under
a path prefix (app.war with /app/solr mounted into it, for example).
You will need to put this prefix in front of the SolrDispatchFilter
url-pattern mapping too (/solr/*), and also on any paths for
legacy Solr servlet mappings you may be using.
For the admin JSP's to work properly in a path-prefixed configuration,
the admin folder containing the JSPs needs to be under the app context root
named to match the path-prefix. For example:
.war
xxx
admin
stats.jsp
-->
<!--
<init-param>
<param-name>path-prefix</param-name>
<param-value>/xxx</param-value>
</init-param>
-->
</filter>
<filter-mapping>
<filter-name>LocalHostRestrictionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<!--
NOTE: When using multicore, /admin JSP URLs with a core specified
such as /solr/coreName/admin/stats.jsp get forwarded by a
RequestDispatcher to /solr/admin/stats.jsp with the specified core
put into request scope keyed as "org.apache.solr.SolrCore".
It is unnecessary, and potentially problematic, to have the SolrDispatchFilter
configured to also filter on forwards. Do not configure
this dispatcher as <dispatcher>FORWARD</dispatcher>.
-->
<filter-name>SolrRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Otherwise it will continue to the old servlets -->
<listener>
<listener-class>org.dspace.solr.filters.ConfigureLog4jListener</listener-class>
</listener>
<servlet>
<servlet-name>SolrServer</servlet-name>
<display-name>Solr</display-name>
<description>Solr Server</description>
<servlet-class>org.apache.solr.servlet.SolrServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>SolrUpdate</servlet-name>
<display-name>SolrUpdate</display-name>
<description>Solr Update Handler</description>
<servlet-class>org.apache.solr.servlet.SolrUpdateServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>Logging</servlet-name>
<servlet-class>org.apache.solr.servlet.LogLevelSelection</servlet-class>
</servlet>
<!-- @Deprecated -->
<servlet>
<servlet-name>ping</servlet-name>
<jsp-file>/admin/ping.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>SolrServer</servlet-name>
<url-pattern>/select/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SolrUpdate</servlet-name>
<url-pattern>/update/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Logging</servlet-name>
<url-pattern>/admin/logging</url-pattern>
</servlet-mapping>
<!-- @Deprecated -->
<servlet-mapping>
<servlet-name>ping</servlet-name>
<url-pattern>/admin/ping</url-pattern>
</servlet-mapping>
<!-- @Deprecated -->
<servlet-mapping>
<servlet-name>Logging</servlet-name>
<url-pattern>/admin/logging.jsp</url-pattern>
</servlet-mapping>
<mime-mapping>
<extension>.xsl</extension>
<!-- per http://www.w3.org/TR/2006/PR-xslt20-20061121/ -->
<mime-type>application/xslt+xml</mime-type>
</mime-mapping>
<!-- People who want to hardcode their "Solr Home" directly into the
WAR File can set the JNDI property here...
-->
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>${dspace.dir}/solr</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
</web-app>