/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL 1.0 (the
* "Licenses"). You can select the license that you prefer but you may not use
* this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/
package org.restlet.resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.ReferenceList;
import org.restlet.engine.local.DirectoryServerResource;
import org.restlet.engine.util.AlphaNumericComparator;
import org.restlet.engine.util.AlphabeticalComparator;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
/**
* Finder mapping a directory of local resources. Those resources have
* representations accessed by the file system, the class loaders or other URI
* accessible protocols. Here is some sample code illustrating how to attach a
* directory to a router:<br>
*
* <pre>
* Directory directory = new Directory(getContext(), "file:///user/data/files/");
* Router router = new Router(getContext());
* router.attach("/static/", directory);
* </pre>
*
* An automatic content negotiation mechanism (similar to the one in Apache HTTP
* server) is used to select the best representation of a resource based on the
* available variants and on the client capabilities and preferences.<br>
* <br>
* The directory can be used in read-only or modifiable mode. In the latter
* case, you just need to set the "modifiable" property to true. The currently
* supported methods are PUT and DELETE.<br>
* <br>
* When no index is available in a given directory, a representation can be
* automatically generated by the
* {@link #getIndexRepresentation(Variant, ReferenceList)} method, unless the
* "listingAllowed" property is turned off. You can even customize the way the
* index entries are sorted by using the {@link #setComparator(Comparator)}
* method. The default sorting uses the friendly Alphanum algorithm based on
* David Koelle's <a href="http://www.davekoelle.com/alphanum.html">original
* idea</a>, using a different and faster implementation contributed by Rob
* Heittman.<br>
* <br>
* Concurrency note: instances of this class or its subclasses can be invoked by
* several threads at the same time and therefore must be thread-safe. You
* should be especially careful when storing state in member variables.
*
* @see <a href="http://wiki.restlet.org/docs_2.1/374-restlet.html">User Guide -
* Serving static files</a>
* @author Jerome Louvel
*/
public class Directory extends Finder {
/** The reference comparator to sort index pages. */
private volatile Comparator<Reference> comparator;
/**
* Indicates if the sub-directories are deeply accessible (true by default).
*/
private volatile boolean deeplyAccessible;
/** The index name, without extensions (ex: "index" or "home"). */
private volatile String indexName;
/**
* Indicates if the display of directory listings is allowed when no index
* file is found.
*/
private volatile boolean listingAllowed;
/**
* Indicates if modifications to local resources are allowed (false by
* default).
*/
private volatile boolean modifiable;
/** Indicates if the best content is automatically negotiated. */
private volatile boolean negotiatingContent;
/** The absolute root reference (file, clap URI). */
private volatile Reference rootRef;
/**
* Constructor.
*
* @param context
* The context.
* @param rootLocalReference
* The root URI.
*/
public Directory(Context context, Reference rootLocalReference) {
super(context);
// First, let's normalize the root reference to prevent any issue with
// relative paths inside the reference leading to listing issues.
final String rootIdentifier = rootLocalReference.getTargetRef()
.getIdentifier();
if (rootIdentifier.endsWith("/")) {
this.rootRef = new Reference(rootIdentifier);
} else {
// We don't take the risk of exposing directory "file:///C:/AA"
// if only "file:///C:/A" was intended
this.rootRef = new Reference(rootIdentifier + "/");
}
this.comparator = new AlphaNumericComparator();
this.deeplyAccessible = true;
this.indexName = "index";
this.listingAllowed = false;
this.modifiable = false;
this.negotiatingContent = true;
setTargetClass(DirectoryServerResource.class);
}
/**
* Constructor.
*
* @param context
* The context.
* @param rootUri
* The absolute root URI. <br>
* <br>
* If you serve files from the file system, use file:// URIs and
* make sure that you register a FILE connector with your parent
* Component. On Windows, make sure that you add enough slash
* characters at the beginning, for example: file:///c:/dir/file<br>
* <br>
* If you serve files from a class loader, use clap:// URIs and
* make sure that you register a CLAP connector with your parent
* Component.<br>
* <br>
*/
public Directory(Context context, String rootUri) {
this(context, new Reference(rootUri));
}
/**
* Returns the reference comparator used to sort index pages. The default
* implementation used a friendly alphanum sorting.
*
* @return The reference comparator.
* @see #setAlphaNumComparator()
*/
public Comparator<Reference> getComparator() {
return this.comparator;
}
/**
* Returns the index name, without extensions. Returns "index" by default.
*
* @return The index name.
*/
public String getIndexName() {
return this.indexName;
}
/**
* Returns an actual index representation for a given variant.
*
* @param variant
* The selected variant.
* @param indexContent
* The directory index to represent.
* @return The actual index representation.
*/
public Representation getIndexRepresentation(Variant variant,
ReferenceList indexContent) {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_HTML)) {
result = indexContent.getWebRepresentation();
} else if (variant.getMediaType().equals(MediaType.TEXT_URI_LIST)) {
result = indexContent.getTextRepresentation();
}
return result;
}
/**
* Returns the variant representations of a directory index. This method can
* be subclassed in order to provide alternative representations.
*
* By default it returns a simple HTML document and a textual URI list as
* variants. Note that a new instance of the list is created for each call.
*
* @param indexContent
* The list of references contained in the directory index.
* @return The variant representations of a directory.
*/
public List<Variant> getIndexVariants(ReferenceList indexContent) {
final List<Variant> result = new ArrayList<Variant>();
result.add(new Variant(MediaType.TEXT_HTML));
result.add(new Variant(MediaType.TEXT_URI_LIST));
return result;
}
/**
* Returns the root URI from which the relative resource URIs will be looked
* up.
*
* @return The root URI.
*/
public Reference getRootRef() {
return this.rootRef;
}
@Override
public void handle(Request request, Response response) {
request.getAttributes().put("org.restlet.directory", this);
super.handle(request, response);
}
/**
* Indicates if the sub-directories are deeply accessible (true by default).
*
* @return True if the sub-directories are deeply accessible.
*/
public boolean isDeeplyAccessible() {
return this.deeplyAccessible;
}
/**
* Indicates if the display of directory listings is allowed when no index
* file is found.
*
* @return True if the display of directory listings is allowed when no
* index file is found.
*/
public boolean isListingAllowed() {
return this.listingAllowed;
}
/**
* Indicates if modifications to local resources (most likely files) are
* allowed. Returns false by default.
*
* @return True if modifications to local resources are allowed.
*/
public boolean isModifiable() {
return this.modifiable;
}
/**
* Indicates if the best content is automatically negotiated. Default value
* is true.
*
* @return True if the best content is automatically negotiated.
*/
public boolean isNegotiatingContent() {
return this.negotiatingContent;
}
/**
* Sets the reference comparator based on classic alphabetical order.
*
* @see #setComparator(Comparator)
*/
public void setAlphaComparator() {
setComparator(new AlphabeticalComparator());
}
/**
* Sets the reference comparator based on the more friendly "Alphanum
* Algorithm" created by David Koelle. The internal implementation used is
* based on an optimized public domain implementation provided by Rob
* Heittman from the Solertium Corporation.
*
* @see <a href="http://www.davekoelle.com/alphanum.html">The original
* Alphanum Algorithm from David Koelle</a>
* @see #setComparator(Comparator)
*/
public void setAlphaNumComparator() {
setComparator(new AlphabeticalComparator());
}
/**
* Sets the reference comparator used to sort index pages.
*
* @param comparator
* The reference comparator.
*/
public void setComparator(Comparator<Reference> comparator) {
this.comparator = comparator;
}
/**
* Indicates if the sub-directories are deeply accessible (true by default).
*
* @param deeplyAccessible
* True if the sub-directories are deeply accessible.
*/
public void setDeeplyAccessible(boolean deeplyAccessible) {
this.deeplyAccessible = deeplyAccessible;
}
/**
* Sets the index name, without extensions.
*
* @param indexName
* The index name.
*/
public void setIndexName(String indexName) {
this.indexName = indexName;
}
/**
* Indicates if the display of directory listings is allowed when no index
* file is found.
*
* @param listingAllowed
* True if the display of directory listings is allowed when no
* index file is found.
*/
public void setListingAllowed(boolean listingAllowed) {
this.listingAllowed = listingAllowed;
}
/**
* Indicates if modifications to local resources are allowed.
*
* @param modifiable
* True if modifications to local resources are allowed.
*/
public void setModifiable(boolean modifiable) {
this.modifiable = modifiable;
}
/**
* Indicates if the best content is automatically negotiated. Default value
* is true.
*
* @param negotiatingContent
* True if the best content is automatically negotiated.
*/
public void setNegotiatingContent(boolean negotiatingContent) {
this.negotiatingContent = negotiatingContent;
}
/**
* Sets the root URI from which the relative resource URIs will be lookep
* up.
*
* @param rootRef
* The root URI.
*/
public void setRootRef(Reference rootRef) {
this.rootRef = rootRef;
}
}