[MRM-781] Removal of Archiva-Webdav implementation in favor of Jackrabbit-webdav

Submitted by: James William Dumay


git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@649352 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brett Porter 2008-04-18 03:05:18 +00:00
parent 4a6a9ff697
commit 558d0c2a4f
103 changed files with 1755 additions and 13730 deletions

View File

@ -255,7 +255,7 @@
</build>
<pluginRepositories>
<pluginRepository>
<id>codehaus.org</id>
<id>codehaus.snapshots</id>
<url>http://snapshots.repository.codehaus.org/</url>
</pluginRepository>
</pluginRepositories>

View File

@ -1,4 +1,4 @@
package org.apache.maven.archiva.web.util;
package org.apache.maven.archiva.security;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@ -27,8 +27,6 @@ import org.codehaus.plexus.redback.system.SecuritySession;
import org.codehaus.plexus.redback.system.SecuritySystemConstants;
import org.codehaus.plexus.redback.users.User;
import com.opensymphony.xwork.ActionContext;
/**
* ArchivaXworkUser
*
@ -37,35 +35,21 @@ import com.opensymphony.xwork.ActionContext;
*/
public class ArchivaXworkUser
{
private static Map<String, Object> getContextSession()
public static String getActivePrincipal( Map<String, Object> sessionMap )
{
ActionContext context = ActionContext.getContext();
Map<String, Object> sessionMap = context.getSession();
if ( sessionMap == null )
{
sessionMap = new HashMap<String, Object>();
return ArchivaRoleConstants.PRINCIPAL_GUEST;
}
return sessionMap;
}
private static SecuritySession getSecuritySession()
{
SecuritySession securitySession =
(SecuritySession) getContextSession().get( SecuritySystemConstants.SECURITY_SESSION_KEY );
(SecuritySession) sessionMap.get( SecuritySystemConstants.SECURITY_SESSION_KEY );
if ( securitySession == null )
{
securitySession = (SecuritySession) getContextSession().get( SecuritySession.ROLE );
securitySession = (SecuritySession) sessionMap.get( SecuritySession.ROLE );
}
return securitySession;
}
public static String getActivePrincipal()
{
SecuritySession securitySession = getSecuritySession();
if ( securitySession == null )
{
return ArchivaRoleConstants.PRINCIPAL_GUEST;

View File

@ -136,16 +136,6 @@
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-http</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-file</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.opensymphony</groupId>
<artifactId>webwork</artifactId>

View File

@ -22,15 +22,13 @@ package org.apache.maven.archiva.web.action;
import java.util.Collections;
import java.util.List;
import com.opensymphony.xwork.ActionContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.database.browsing.BrowsingResults;
import org.apache.maven.archiva.database.browsing.RepositoryBrowsing;
import org.apache.maven.archiva.security.AccessDeniedException;
import org.apache.maven.archiva.security.ArchivaSecurityException;
import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.apache.maven.archiva.web.util.ArchivaXworkUser;
import org.apache.maven.archiva.security.*;
import org.apache.maven.archiva.security.ArchivaXworkUser;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
/**
@ -121,7 +119,7 @@ public class BrowseAction
private String getPrincipal()
{
return ArchivaXworkUser.getActivePrincipal();
return ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
private List<String> getObservableRepos()

View File

@ -23,6 +23,7 @@ import java.net.MalformedURLException;
import java.util.Collections;
import java.util.List;
import com.opensymphony.xwork.ActionContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.database.ArchivaDAO;
@ -33,11 +34,8 @@ import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
import org.apache.maven.archiva.indexer.search.CrossRepositorySearch;
import org.apache.maven.archiva.indexer.search.SearchResultLimits;
import org.apache.maven.archiva.indexer.search.SearchResults;
import org.apache.maven.archiva.security.AccessDeniedException;
import org.apache.maven.archiva.security.ArchivaSecurityException;
import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.apache.maven.archiva.web.util.ArchivaXworkUser;
import org.apache.maven.archiva.security.*;
import org.apache.maven.archiva.security.ArchivaXworkUser;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
/**
@ -155,7 +153,7 @@ public class SearchAction
private String getPrincipal()
{
return ArchivaXworkUser.getActivePrincipal();
return ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
private List<String> getObservableRepos()

View File

@ -31,9 +31,10 @@ import org.apache.maven.archiva.security.AccessDeniedException;
import org.apache.maven.archiva.security.ArchivaSecurityException;
import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.apache.maven.archiva.web.util.ArchivaXworkUser;
import org.apache.maven.archiva.security.ArchivaXworkUser;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.Validateable;
/**
@ -173,7 +174,7 @@ public class ShowArtifactAction
private String getPrincipal()
{
return ArchivaXworkUser.getActivePrincipal();
return ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
private List<String> getObservableRepos()

View File

@ -51,9 +51,10 @@ import org.apache.maven.archiva.repository.project.writers.ProjectModel400Writer
import org.apache.maven.archiva.security.ArchivaSecurityException;
import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.apache.maven.archiva.web.util.ArchivaXworkUser;
import org.apache.maven.archiva.security.ArchivaXworkUser;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.Preparable;
import com.opensymphony.xwork.Validateable;
@ -325,7 +326,7 @@ public class UploadAction
private String getPrincipal()
{
return ArchivaXworkUser.getActivePrincipal();
return ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
private void copyFile( File targetPath, String artifactFilename )

View File

@ -1,598 +0,0 @@
package org.apache.maven.archiva.web.repository;
/*
* 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.
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.maven.archiva.common.utils.PathUtil;
import org.apache.maven.archiva.model.ArtifactReference;
import org.apache.maven.archiva.model.ProjectReference;
import org.apache.maven.archiva.model.VersionedReference;
import org.apache.maven.archiva.policies.ProxyDownloadException;
import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
import org.apache.maven.archiva.repository.ManagedRepositoryContent;
import org.apache.maven.archiva.repository.RepositoryContentFactory;
import org.apache.maven.archiva.repository.RepositoryException;
import org.apache.maven.archiva.repository.RepositoryNotFoundException;
import org.apache.maven.archiva.repository.audit.AuditEvent;
import org.apache.maven.archiva.repository.audit.AuditListener;
import org.apache.maven.archiva.repository.audit.Auditable;
import org.apache.maven.archiva.repository.content.RepositoryRequest;
import org.apache.maven.archiva.repository.layout.LayoutException;
import org.apache.maven.archiva.repository.metadata.MetadataTools;
import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
import org.apache.maven.archiva.web.util.ArchivaXworkUser;
import org.apache.maven.archiva.webdav.AbstractDavServerComponent;
import org.apache.maven.archiva.webdav.DavServerComponent;
import org.apache.maven.archiva.webdav.DavServerException;
import org.apache.maven.archiva.webdav.DavServerListener;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Relocation;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
/**
* ProxiedDavServer
*
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
* @version $Id$
* @plexus.component role="org.apache.maven.archiva.webdav.DavServerComponent"
* role-hint="proxied" instantiation-strategy="per-lookup"
*/
public class ProxiedDavServer
extends AbstractDavServerComponent
implements Auditable
{
/**
* @plexus.requirement role-hint="simple"
*/
private DavServerComponent davServer;
/**
* @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
*/
private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
/**
* @plexus.requirement
*/
private RepositoryContentFactory repositoryFactory;
/**
* @plexus.requirement
*/
private RepositoryRequest repositoryRequest;
/**
* @plexus.requirement role-hint="default"
*/
private RepositoryProxyConnectors connectors;
/**
* @plexus.requirement
*/
private MetadataTools metadataTools;
private ManagedRepositoryContent managedRepository;
public String getPrefix()
{
return davServer.getPrefix();
}
public File getRootDirectory()
{
return davServer.getRootDirectory();
}
public void setPrefix( String prefix )
{
davServer.setPrefix( prefix );
}
public void setRootDirectory( File rootDirectory )
{
davServer.setRootDirectory( rootDirectory );
}
public void init( ServletConfig servletConfig )
throws DavServerException
{
davServer.init( servletConfig );
try
{
managedRepository = repositoryFactory.getManagedRepositoryContent( getPrefix() );
}
catch ( RepositoryNotFoundException e )
{
throw new DavServerException( e.getMessage(), e );
}
catch ( RepositoryException e )
{
throw new DavServerException( e.getMessage(), e );
}
}
public void process( DavServerRequest request, HttpServletResponse response )
throws DavServerException, ServletException, IOException
{
boolean isGet = WebdavMethodUtil.isReadMethod( request.getRequest().getMethod() );
boolean isPut = WebdavMethodUtil.isWriteMethod( request.getRequest().getMethod() );
String resource = request.getLogicalResource();
if ( isGet )
{
// Default behaviour is to treat the resource natively.
File resourceFile = new File( managedRepository.getRepoRoot(), resource );
// If this a directory resource, then we are likely browsing.
if ( resourceFile.exists() && resourceFile.isDirectory() )
{
String requestURL = request.getRequest().getRequestURL().toString();
// [MRM-440] - If webdav URL lacks a trailing /, navigating to
// all links in the listing return 404.
if ( !requestURL.endsWith( "/" ) )
{
String redirectToLocation = requestURL + "/";
response.sendRedirect( redirectToLocation );
return;
}
// Process the request.
davServer.process( request, response );
// All done.
return;
}
// At this point the incoming request can either be in default or
// legacy layout format.
try
{
boolean fromProxy = fetchContentFromProxies( request, resource );
// Perform an adjustment of the resource to the managed
// repository expected path.
resource =
repositoryRequest
.toNativePath( request.getLogicalResource(), managedRepository );
resourceFile = new File( managedRepository.getRepoRoot(), resource );
// Adjust the pathInfo resource to be in the format that the dav
// server impl expects.
request.setLogicalResource( resource );
boolean previouslyExisted = resourceFile.exists();
// Attempt to fetch the resource from any defined proxy.
if ( fromProxy )
{
processAuditEvents( request, resource, previouslyExisted, resourceFile,
" (proxied)" );
}
}
catch ( LayoutException e )
{
// Invalid resource, pass it on.
respondResourceMissing( request, response, e );
// All done.
return;
}
if ( resourceFile.exists() )
{
// [MRM-503] - Metadata file need Pragma:no-cache response
// header.
if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) )
{
response.addHeader( "Pragma", "no-cache" );
response.addHeader( "Cache-Control", "no-cache" );
}
// TODO: [MRM-524] determine http caching options for other
// types of files (artifacts, sha1, md5, snapshots)
davServer.process( request, response );
}
else
{
respondResourceMissing( request, response, null );
}
}
if ( isPut )
{
/*
* Create parent directories that don't exist when writing a file
* This actually makes this implementation not compliant to the
* WebDAV RFC - but we have enough knowledge about how the
* collection is being used to do this reasonably and some versions
* of Maven's WebDAV don't correctly create the collections
* themselves.
*/
File rootDirectory = getRootDirectory();
if ( rootDirectory != null )
{
File destDir = new File( rootDirectory, resource ).getParentFile();
if ( !destDir.exists() )
{
destDir.mkdirs();
String relPath =
PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
triggerAuditEvent( request, relPath, AuditEvent.CREATE_DIR );
}
}
File resourceFile = new File( managedRepository.getRepoRoot(), resource );
boolean previouslyExisted = resourceFile.exists();
// Allow the dav server to process the put request.
davServer.process( request, response );
processAuditEvents( request, resource, previouslyExisted, resourceFile, null );
// All done.
return;
}
}
private void respondResourceMissing( DavServerRequest request, HttpServletResponse response,
Throwable t )
{
response.setStatus( HttpServletResponse.SC_NOT_FOUND );
try
{
StringBuffer missingUrl = new StringBuffer();
missingUrl.append( request.getRequest().getScheme() ).append( "://" );
missingUrl.append( request.getRequest().getServerName() ).append( ":" );
missingUrl.append( request.getRequest().getServerPort() );
missingUrl.append( request.getRequest().getServletPath() );
String message = "Error 404 Not Found";
PrintWriter out = new PrintWriter( response.getOutputStream() );
response.setContentType( "text/html; charset=\"UTF-8\"" );
out.println( "<html>" );
out.println( "<head><title>" + message + "</title></head>" );
out.println( "<body>" );
out.print( "<p><h1>" );
out.print( message );
out.println( "</h1></p>" );
out.print( "<p>The following resource does not exist: <a href=\"" );
out.print( missingUrl.toString() );
out.println( "\">" );
out.print( missingUrl.toString() );
out.println( "</a></p>" );
if ( t != null )
{
out.println( "<pre>" );
t.printStackTrace( out );
out.println( "</pre>" );
}
out.println( "</body></html>" );
out.flush();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
private boolean fetchContentFromProxies( DavServerRequest request, String resource )
throws ServletException
{
if ( repositoryRequest.isSupportFile( resource ) )
{
// Checksums are fetched with artifact / metadata.
// Need to adjust the path for the checksum resource.
return false;
}
// Is it a Metadata resource?
if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) )
{
return fetchMetadataFromProxies( request, resource );
}
// Not any of the above? Then it's gotta be an artifact reference.
try
{
// Get the artifact reference in a layout neutral way.
ArtifactReference artifact = repositoryRequest.toArtifactReference( resource );
if ( artifact != null )
{
applyServerSideRelocation( artifact );
File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
// Set the path to the resource using managed repository
// specific layout format.
request.setLogicalResource( managedRepository.toPath( artifact ) );
return ( proxiedFile != null );
}
}
catch ( LayoutException e )
{
/* eat it */
}
catch ( ProxyDownloadException e )
{
throw new ServletException( "Unable to fetch artifact resource.", e );
}
return false;
}
private boolean fetchMetadataFromProxies( DavServerRequest request, String resource )
throws ServletException
{
ProjectReference project;
VersionedReference versioned;
try
{
versioned = metadataTools.toVersionedReference( resource );
if ( versioned != null )
{
connectors.fetchFromProxies( managedRepository, versioned );
return true;
}
}
catch ( RepositoryMetadataException e )
{
/* eat it */
}
try
{
project = metadataTools.toProjectReference( resource );
if ( project != null )
{
connectors.fetchFromProxies( managedRepository, project );
return true;
}
}
catch ( RepositoryMetadataException e )
{
/* eat it */
}
return false;
}
/**
* A relocation capable client will request the POM prior to the artifact,
* and will then read meta-data and do client side relocation. A simplier
* client (like maven 1) will only request the artifact and not use the
* metadatas.
* <p>
* For such clients, archiva does server-side relocation by reading itself
* the &lt;relocation&gt; element in metadatas and serving the expected
* artifact.
*/
protected void applyServerSideRelocation( ArtifactReference artifact )
throws ProxyDownloadException
{
if ( "pom".equals( artifact.getType() ) )
{
return;
}
// Build the artifact POM reference
ArtifactReference pomReference = new ArtifactReference();
pomReference.setGroupId( artifact.getGroupId() );
pomReference.setArtifactId( artifact.getArtifactId() );
pomReference.setVersion( artifact.getVersion() );
pomReference.setType( "pom" );
// Get the artifact POM from proxied repositories if needed
connectors.fetchFromProxies( managedRepository, pomReference );
// Open and read the POM from the managed repo
File pom = managedRepository.toFile( pomReference );
if ( !pom.exists() )
{
return;
}
try
{
Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
DistributionManagement dist = model.getDistributionManagement();
if ( dist != null )
{
Relocation relocation = dist.getRelocation();
if ( relocation != null )
{
// artifact is relocated : update the repositoryPath
if ( relocation.getGroupId() != null )
{
artifact.setGroupId( relocation.getGroupId() );
}
if ( relocation.getArtifactId() != null )
{
artifact.setArtifactId( relocation.getArtifactId() );
}
if ( relocation.getVersion() != null )
{
artifact.setVersion( relocation.getVersion() );
}
}
}
}
catch ( FileNotFoundException e )
{
// Artifact has no POM in repo : ignore
}
catch ( IOException e )
{
// Unable to read POM : ignore.
}
catch ( XmlPullParserException e )
{
// Invalid POM : ignore
}
}
@Override
public void addListener( DavServerListener listener )
{
super.addListener( listener );
davServer.addListener( listener );
}
@Override
public boolean isUseIndexHtml()
{
return davServer.isUseIndexHtml();
}
@Override
public boolean hasResource( String resource )
{
return davServer.hasResource( resource );
}
@Override
public void removeListener( DavServerListener listener )
{
davServer.removeListener( listener );
}
@Override
public void setUseIndexHtml( boolean useIndexHtml )
{
super.setUseIndexHtml( useIndexHtml );
davServer.setUseIndexHtml( useIndexHtml );
}
public ManagedRepositoryContent getRepository()
{
return managedRepository;
}
private void processAuditEvents( DavServerRequest request, String resource,
boolean previouslyExisted, File resourceFile, String suffix )
{
if ( suffix == null )
{
suffix = "";
}
// Process Create Audit Events.
if ( !previouslyExisted && resourceFile.exists() )
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, resource, AuditEvent.CREATE_FILE + suffix );
}
else if ( resourceFile.isDirectory() )
{
triggerAuditEvent( request, resource, AuditEvent.CREATE_DIR + suffix );
}
}
// Process Remove Audit Events.
else if ( previouslyExisted && !resourceFile.exists() )
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, resource, AuditEvent.REMOVE_FILE + suffix );
}
else if ( resourceFile.isDirectory() )
{
triggerAuditEvent( request, resource, AuditEvent.REMOVE_DIR + suffix );
}
}
// Process modify events.
else
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, resource, AuditEvent.MODIFY_FILE + suffix );
}
}
}
private void triggerAuditEvent( String user, String remoteIP, String resource, String action )
{
AuditEvent event = new AuditEvent( this.getPrefix(), user, resource, action );
event.setRemoteIP( remoteIP );
for ( AuditListener listener : auditListeners )
{
listener.auditEvent( event );
}
}
private void triggerAuditEvent( DavServerRequest request, String resource, String action )
{
triggerAuditEvent( ArchivaXworkUser.getActivePrincipal(), getRemoteIP( request ), resource,
action );
}
private String getRemoteIP( DavServerRequest request )
{
return request.getRequest().getRemoteAddr();
}
public void addAuditListener( AuditListener listener )
{
this.auditListeners.add( listener );
}
public void clearAuditListeners()
{
this.auditListeners.clear();
}
public void removeAuditListener( AuditListener listener )
{
this.auditListeners.remove( listener );
}
}

View File

@ -23,29 +23,24 @@ import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import org.apache.maven.archiva.configuration.ConfigurationEvent;
import org.apache.maven.archiva.configuration.ConfigurationListener;
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
import org.apache.maven.archiva.security.ArchivaRoleConstants;
import org.apache.maven.archiva.webdav.DavServerComponent;
import org.apache.maven.archiva.webdav.DavServerException;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.servlet.multiplexed.MultiplexedWebDavServlet;
import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
import org.codehaus.plexus.redback.authentication.AuthenticationException;
import org.codehaus.plexus.redback.authentication.AuthenticationResult;
import org.codehaus.plexus.redback.authorization.AuthorizationException;
import org.codehaus.plexus.redback.authorization.AuthorizationResult;
import org.codehaus.plexus.redback.policy.AccountLockedException;
import org.codehaus.plexus.redback.policy.MustChangePasswordException;
import org.codehaus.plexus.redback.system.SecuritySession;
import org.apache.maven.archiva.webdav.ArchivaDavLocatorFactory;
import org.apache.maven.archiva.webdav.ArchivaDavResourceFactory;
import org.apache.maven.archiva.webdav.ArchivaDavSessionProvider;
import org.apache.maven.archiva.webdav.UnauthorizedDavException;
import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
import org.apache.jackrabbit.webdav.*;
import org.codehaus.plexus.redback.system.SecuritySystem;
import org.codehaus.plexus.redback.xwork.filter.authentication.HttpAuthenticator;
import org.codehaus.plexus.spring.PlexusToSpringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Map;
@ -57,9 +52,11 @@ import java.util.Map;
* @version $Id$
*/
public class RepositoryServlet
extends MultiplexedWebDavServlet
extends AbstractWebdavServlet
implements ConfigurationListener
{
private Logger log = LoggerFactory.getLogger(RepositoryServlet.class);
private SecuritySystem securitySystem;
private HttpAuthenticator httpAuth;
@ -67,17 +64,76 @@ public class RepositoryServlet
private ArchivaConfiguration configuration;
private Map<String, ManagedRepositoryConfiguration> repositoryMap;
private ArchivaMimeTypeLoader mimeTypeLoader;
private DavLocatorFactory locatorFactory;
private DavResourceFactory resourceFactory;
private DavSessionProvider sessionProvider;
private final Object reloadLock = new Object();
public void init(javax.servlet.ServletConfig servletConfig)
throws ServletException
{
super.init(servletConfig);
initServers(servletConfig);
}
/**
* Service the given request.
*
* @param request
* @param response
* @throws ServletException
* @throws java.io.IOException
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
WebdavRequest webdavRequest = new WebdavRequestImpl(request, getLocatorFactory());
// DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'.
int methodCode = DavMethods.getMethodCode(request.getMethod());
boolean noCache = DavMethods.isDeltaVMethod(webdavRequest) && !(DavMethods.DAV_VERSION_CONTROL == methodCode || DavMethods.DAV_REPORT == methodCode);
WebdavResponse webdavResponse = new WebdavResponseImpl(response, noCache);
try {
// make sure there is a authenticated user
if (!getDavSessionProvider().attachSession(webdavRequest)) {
return;
}
// check matching if=header for lock-token relevant operations
DavResource resource = getResourceFactory().createResource(webdavRequest.getRequestLocator(), webdavRequest, webdavResponse);
if (!isPreconditionValid(webdavRequest, resource)) {
webdavResponse.sendError(DavServletResponse.SC_PRECONDITION_FAILED);
return;
}
if (!execute(webdavRequest, webdavResponse, methodCode, resource)) {
super.service(request, response);
}
}
catch (UnauthorizedDavException e)
{
webdavResponse.setHeader("WWW-Authenticate", getAuthenticateHeaderValue(e.getRepositoryName()));
webdavResponse.sendError(e.getErrorCode(), e.getStatusPhrase());
}
catch (DavException e) {
if (e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED) {
log.error("Should throw UnauthorizedDavException");
} else {
webdavResponse.sendError(e);
}
} finally {
getDavSessionProvider().releaseSession(webdavRequest);
}
}
public synchronized void initServers( ServletConfig servletConfig )
throws DavServerException
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() );
mimeTypeLoader = (ArchivaMimeTypeLoader) wac.getBean(
PlexusToSpringUtils.buildSpringId( ArchivaMimeTypeLoader.class.getName() ) );
securitySystem = (SecuritySystem) wac.getBean( PlexusToSpringUtils.buildSpringId( SecuritySystem.ROLE ) );
httpAuth =
(HttpAuthenticator) wac.getBean( PlexusToSpringUtils.buildSpringId( HttpAuthenticator.ROLE, "basic" ) );
@ -101,120 +157,11 @@ public class RepositoryServlet
continue;
}
}
DavServerComponent server = createServer( repo.getId(), repoDir, servletConfig );
server.setUseIndexHtml( true );
}
}
@Override
protected void service( HttpServletRequest httpRequest, HttpServletResponse httpResponse )
throws ServletException, IOException
{
// Wrap the incoming request to adjust paths and whatnot.
super.service( new PolicingServletRequest( httpRequest ), httpResponse );
}
public synchronized ManagedRepositoryConfiguration getRepository( String prefix )
{
if ( repositoryMap.isEmpty() )
{
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() );
}
return repositoryMap.get( prefix );
}
private String getRepositoryName( DavServerRequest request )
{
ManagedRepositoryConfiguration repoConfig = getRepository( request.getPrefix() );
if ( repoConfig == null )
{
return "Unknown";
}
return repoConfig.getName();
}
public boolean isAuthenticated( DavServerRequest davRequest, HttpServletResponse response )
throws ServletException, IOException
{
HttpServletRequest request = davRequest.getRequest();
// Authentication Tests.
try
{
AuthenticationResult result = httpAuth.getAuthenticationResult( request, response );
if ( result != null && !result.isAuthenticated() )
{
// Must Authenticate.
httpAuth.challenge( request, response, "Repository " + getRepositoryName( davRequest ),
new AuthenticationException( "User Credentials Invalid" ) );
return false;
}
}
catch ( AuthenticationException e )
{
log( "Fatal Http Authentication Error.", e );
throw new ServletException( "Fatal Http Authentication Error.", e );
}
catch ( AccountLockedException e )
{
httpAuth.challenge( request, response, "Repository " + getRepositoryName( davRequest ),
new AuthenticationException( "User account is locked" ) );
}
catch ( MustChangePasswordException e )
{
httpAuth.challenge( request, response, "Repository " + getRepositoryName( davRequest ),
new AuthenticationException( "You must change your password." ) );
}
return true;
}
public boolean isAuthorized( DavServerRequest davRequest, HttpServletResponse response )
throws ServletException, IOException
{
// Authorization Tests.
HttpServletRequest request = davRequest.getRequest();
boolean isWriteRequest = WebdavMethodUtil.isWriteMethod( request.getMethod() );
SecuritySession securitySession = httpAuth.getSecuritySession();
try
{
String permission = ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS;
if ( isWriteRequest )
{
permission = ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD;
}
AuthorizationResult authzResult =
securitySystem.authorize( securitySession, permission, davRequest.getPrefix() );
if ( !authzResult.isAuthorized() )
{
if ( authzResult.getException() != null )
{
log( "Authorization Denied [ip=" + request.getRemoteAddr() + ",isWriteRequest=" + isWriteRequest +
",permission=" + permission + ",repo=" + davRequest.getPrefix() + "] : " +
authzResult.getException().getMessage() );
}
// Issue HTTP Challenge.
httpAuth.challenge( request, response, "Repository " + getRepositoryName( davRequest ),
new AuthenticationException( "Authorization Denied." ) );
return false;
}
}
catch ( AuthorizationException e )
{
throw new ServletException( "Fatal Authorization Subsystem Error." );
}
return true;
resourceFactory = (DavResourceFactory)wac.getBean(PlexusToSpringUtils.buildSpringId(ArchivaDavResourceFactory.class));
locatorFactory = new ArchivaDavLocatorFactory();
sessionProvider = new ArchivaDavSessionProvider(wac);
}
public void configurationEvent( ConfigurationEvent event )
@ -233,25 +180,68 @@ public class RepositoryServlet
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() );
}
synchronized ( davManager )
synchronized ( reloadLock )
{
// Clear out the old servers.
davManager.removeAllServers();
// Create new servers.
try
{
initServers( getServletConfig() );
}
catch ( DavServerException e )
{
log( "Unable to init servers: " + e.getMessage(), e );
}
initServers( getServletConfig() );
}
}
public synchronized ManagedRepositoryConfiguration getRepository( String prefix )
{
if ( repositoryMap.isEmpty() )
{
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() );
}
return repositoryMap.get( prefix );
}
ArchivaConfiguration getConfiguration()
{
return configuration;
}
protected boolean isPreconditionValid(final WebdavRequest request, final DavResource davResource)
{
return true;
}
public DavSessionProvider getDavSessionProvider()
{
return sessionProvider;
}
public void setDavSessionProvider(final DavSessionProvider davSessionProvider)
{
this.sessionProvider = davSessionProvider;
}
public DavLocatorFactory getLocatorFactory()
{
return locatorFactory;
}
public void setLocatorFactory(final DavLocatorFactory davLocatorFactory)
{
locatorFactory = davLocatorFactory;
}
public DavResourceFactory getResourceFactory()
{
return resourceFactory;
}
public void setResourceFactory(final DavResourceFactory davResourceFactory)
{
resourceFactory = davResourceFactory;
}
public String getAuthenticateHeaderValue()
{
throw new UnsupportedOperationException("");
}
public String getAuthenticateHeaderValue(String repository)
{
return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\"";
}
}

View File

@ -66,19 +66,6 @@
</configuration>
</component>
<component>
<role>org.apache.maven.archiva.webdav.DavServerManager</role>
<role-hint>default</role-hint>
<implementation>org.apache.maven.archiva.webdav.DefaultDavServerManager</implementation>
<description>DefaultDavServerManager</description>
<requirements>
<requirement>
<role>org.apache.maven.archiva.webdav.DavServerComponent</role>
<role-hint>proxied</role-hint>
</requirement>
</requirements>
</component>
<component>
<role>org.codehaus.plexus.jdo.JdoFactory</role>
<role-hint>archiva</role-hint>
@ -187,14 +174,14 @@
</configuration>
</component>
<component>
<!-- <component>
<role>org.apache.maven.archiva.webdav.util.MimeTypes</role>
<implementation>org.apache.maven.archiva.webdav.util.MimeTypes</implementation>
<description>MimeTypes</description>
<configuration>
<resource>archiva-mime-types.txt</resource>
</configuration>
</component>
</component> -->
<!--
| Logger manager

View File

@ -28,13 +28,13 @@ import org.codehaus.plexus.spring.PlexusInSpringTestCase;
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
* @version $Id$
*/
public class ArchivaMimeTypeLoaderTest
public class MimeTypesLoaderTest
extends PlexusInSpringTestCase
{
public void testArchivaTypes()
throws Exception
{
lookup( ArchivaMimeTypeLoader.class );
lookup( MimeTypes.class );
MimeTypes mimeTypes = (MimeTypes) lookup( MimeTypes.class );
assertNotNull( mimeTypes );
@ -43,5 +43,6 @@ public class ArchivaMimeTypeLoaderTest
assertEquals( "md5", "text/plain", mimeTypes.getMimeType( "foo.md5" ) );
assertEquals( "pgp", "application/pgp-encrypted", mimeTypes.getMimeType( "foo.pgp" ) );
assertEquals( "jar", "application/java-archive", mimeTypes.getMimeType( "foo.jar" ) );
assertEquals( "Default", "application/octet-stream", mimeTypes.getMimeType(".SomeUnknownExtension"));
}
}

View File

@ -0,0 +1,50 @@
package org.apache.maven.archiva.web.repository;
/*
* 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.
*/
import org.apache.maven.archiva.webdav.ArchivaDavSessionProvider;
import org.apache.jackrabbit.webdav.WebdavRequest;
import org.apache.jackrabbit.webdav.DavException;
import org.springframework.web.context.WebApplicationContext;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class UnauthenticatedDavSessionProvider extends ArchivaDavSessionProvider
{
public UnauthenticatedDavSessionProvider(WebApplicationContext applicationContext)
{
super(applicationContext);
}
@Override
protected boolean isAuthorized( WebdavRequest request, String repositoryId )
throws DavException
{
return true;
}
@Override
protected boolean isAuthenticated( WebdavRequest request, String repositoryId )
throws DavException
{
return true;
}
}

View File

@ -19,11 +19,10 @@ package org.apache.maven.archiva.web.repository;
* under the License.
*/
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletConfig;
/**
* UnauthenticatedRepositoryServlet
@ -34,16 +33,14 @@ import java.io.IOException;
public class UnauthenticatedRepositoryServlet
extends RepositoryServlet
{
public boolean isAuthorized( DavServerRequest davRequest, HttpServletResponse response )
throws ServletException, IOException
{
return true;
}
@Override
public boolean isAuthenticated( DavServerRequest davRequest, HttpServletResponse response )
throws ServletException, IOException
public synchronized void initServers( ServletConfig servletConfig )
{
return true;
super.initServers(servletConfig);
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() );
UnauthenticatedDavSessionProvider sessionProvider = new UnauthenticatedDavSessionProvider(wac);
setDavSessionProvider(sessionProvider);
}
}

View File

@ -27,9 +27,45 @@
</parent>
<artifactId>archiva-webdav</artifactId>
<name>Archiva WebDAV Provider</name>
<name>Archiva Web :: WebDAV</name>
<dependencies>
<!-- Archiva Modules -->
<dependency>
<groupId>org.apache.archiva</groupId>
<artifactId>archiva-repository-layer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.archiva</groupId>
<artifactId>archiva-proxy</artifactId>
</dependency>
<dependency>
<groupId>org.apache.archiva</groupId>
<artifactId>archiva-security</artifactId>
</dependency>
<dependency>
<groupId>org.apache.archiva</groupId>
<artifactId>archiva-configuration</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus.redback</groupId>
<artifactId>redback-xwork-integration</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- end Archiva Modules -->
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-http</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-file</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
@ -37,51 +73,40 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-spring</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-webdav</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- We import these classes directly to be able to patch them, since this library hasn't been released in some time
<dependency>
<groupId>it.could</groupId>
<artifactId>webdav</artifactId>
<version>0.4</version>
</dependency>
-->
<!-- Required by it.could classes -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>slide</groupId>
<artifactId>slide-webdavlib</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Required by it.could classes -->

View File

@ -1,125 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* <p>An utility class providing various static methods operating on
* {@link InputStream input} and {@link OutputStream output} streams.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public final class StreamTools {
/** <p>Deny construction.</p> */
private StreamTools() { };
/**
* <p>Copy every byte from the specified {@link InputStream} to the specifed
* {@link OutputStream} and then close both of them.</p>
*
* <p>This method is equivalent to a call to the following method:
* {@link #copy(InputStream,OutputStream,boolean) copy(in, out, true)}.</p>
*
* @param in the {@link InputStream} to read bytes from.
* @param out the {@link OutputStream} to write bytes to.
* @return the number of bytes copied.
* @throws IOException if an I/O error occurred copying the data.
*/
public static long copy(InputStream in, OutputStream out)
throws IOException {
return copy(in, out, true);
}
/**
* <p>Copy every byte from the specified {@link InputStream} to the specifed
* {@link OutputStream} and then optionally close both of them.</p>
*
* @param in the {@link InputStream} to read bytes from.
* @param out the {@link OutputStream} to write bytes to.
* @param close whether to close the streams or not.
* @return the number of bytes copied.
* @throws IOException if an I/O error occurred copying the data.
*/
public static long copy(InputStream in, OutputStream out, boolean close)
throws IOException {
if (in == null) throw new NullPointerException("Null input");
if (out == null) throw new NullPointerException("Null output");
final byte buffer[] = new byte[4096];
int length = -1;
long total = 0;
while ((length = in.read(buffer)) >= 0) {
out.write(buffer, 0, length);
total += length;
}
if (close) {
in.close();
out.close();
}
return total;
}
/**
* Closes the output stream. The output stream can be null and any IOException's will be swallowed.
*
* @param outputStream The stream to close.
*/
public static void close( OutputStream outputStream )
{
if ( outputStream == null )
{
return;
}
try
{
outputStream.close();
}
catch( IOException ex )
{
// ignore
}
}
/**
* Closes the input stream. The input stream can be null and any IOException's will be swallowed.
*
* @param inputStream The stream to close.
*/
public static void close( InputStream inputStream )
{
if ( inputStream == null )
{
return;
}
try
{
inputStream.close();
}
catch( IOException ex )
{
// ignore
}
}
}

View File

@ -1,214 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util;
import it.could.util.encoding.Encodable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>An utility class providing various static methods operating on
* {@link String}s.</p>
*
* <p>This class implement the {@link Encodable} interface from which it
* inherits its {@link Encodable#DEFAULT_ENCODING default encoding}.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public final class StringTools {
/** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
private static final String FORMAT_822 = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";
/** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
private static final String FORMAT_ISO = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/** <p>The {@link TimeZone} to use for dates.</p> */
private static final TimeZone TIMEZONE = TimeZone.getTimeZone("GMT");
/** <p>The {@link Locale} to use for dates.</p> */
private static final Locale LOCALE = Locale.US;
/** <p>Deny construction.</p> */
private StringTools() { }
/* ====================================================================== */
/* NUMBER AND DATE PARSING AND FORMATTING */
/* ====================================================================== */
/**
* <p>Format a {@link Number} into a {@link String} making sure that
* {@link NullPointerException}s are not thrown.</p>
*
* @param number the {@link Number} to format.
* @return a {@link String} instance or <b>null</b> if the object was null.
*/
public static String formatNumber(Number number) {
if (number == null) return null;
return (number.toString());
}
/**
* <p>Parse a {@link String} into a {@link Long}.</p>
*
* @param string the {@link String} to parse.
* @return a {@link Long} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Long parseNumber(String string) {
if (string == null) return null;
try {
return new Long(string);
} catch (NumberFormatException exception) {
return null;
}
}
/**
* <p>Format a {@link Date} according to the HTTP/1.1 RFC.</p>
*
* @param date the {@link Date} to format.
* @return a {@link String} instance or <b>null</b> if the date was null.
*/
public static String formatHttpDate(Date date) {
if (date == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
formatter.setTimeZone(TIMEZONE);
return formatter.format(date);
}
/**
* <p>Format a {@link Date} according to the ISO 8601 specification.</p>
*
* @param date the {@link Date} to format.
* @return a {@link String} instance or <b>null</b> if the date was null.
*/
public static String formatIsoDate(Date date) {
if (date == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
formatter.setTimeZone(TIMEZONE);
return formatter.format(date);
}
/**
* <p>Parse a {@link String} into a {@link Date} according to the
* HTTP/1.1 RFC (<code>Mon, 31 Jan 2000 11:59:00 GMT</code>).</p>
*
* @param string the {@link String} to parse.
* @return a {@link Date} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Date parseHttpDate(String string) {
if (string == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
formatter.setTimeZone(TIMEZONE);
try {
return formatter.parse(string);
} catch (ParseException exception) {
return null;
}
}
/**
* <p>Parse a {@link String} into a {@link Date} according to the ISO 8601
* specification (<code>2000-12-31T11:59:00Z</code>).</p>
*
* @param string the {@link String} to parse.
* @return a {@link Date} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Date parseIsoDate(String string) {
if (string == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
formatter.setTimeZone(TIMEZONE);
try {
return formatter.parse(string);
} catch (ParseException exception) {
return null;
}
}
/* ====================================================================== */
/* STRING SPLITTING */
/* ====================================================================== */
/**
* <p>Split the specified string in two parts according to the specified
* delimiter, and any resulting path of zero length will be converted to
* <b>null</b>.</p>
*/
public static String[] splitOnce(String source, char delimiter,
boolean noDelimReturnSecond) {
if (source == null) return new String[] { null, null };
final int position = source.indexOf(delimiter);
if (position < 0) { // --> first
if (noDelimReturnSecond) return new String[] { null, source };
else return new String[] { source, null };
} else if (position == 0) {
if (source.length() == 1) { // --> |
return new String[] { null, null };
} else { // --> |second
return new String[] { null, source.substring(1) };
}
} else {
final String first = source.substring(0, position);
if (source.length() -1 == position) { // --> first|
return new String[] { first, null };
} else { // --> first|second
return new String[] { first, source.substring(position + 1) };
}
}
}
/**
* <p>Split the specified string according to the specified delimiter, and
* any resulting path of zero length will be converted to <b>null</b>.</p>
*/
public static String[] splitAll(String source, char delimiter) {
final List strings = new ArrayList();
String current = source;
while (current != null) {
String split[] = splitOnce(current, delimiter, false);
strings.add(split[0]);
current = split[1];
}
if (current != null) strings.add(current);
final int length = source.length();
if ((length > 0) && (source.charAt(length - 1) == delimiter)) {
strings.add(null);
}
return (String []) strings.toArray(new String[strings.size()]);
}
/**
* <p>Find the first occurrence of one of the specified delimiter characters
* in the specified source string.</p>
*/
public static int findFirst(String source, String delimiters) {
final char array[] = source.toCharArray();
final char delim[] = delimiters.toCharArray();
for (int x = 0; x < array.length; x ++) {
for (int y = 0; y < delim.length; y ++) {
if (array[x] == delim[y]) return x;
}
}
return -1;
}
}

View File

@ -1,48 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.encoding;
import java.io.UnsupportedEncodingException;
/**
* <p>The {@link Encodable} interface describes an {@link Object} whose
* {@link String} representation can vary depending on the encoding used.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public interface Encodable extends EncodingAware {
/**
* <p>Return the {@link String} representation of this instance.</p>
*
* <p>This method is equivalent to a call to
* {@link #toString(String) toString}({@link EncodingAware#DEFAULT_ENCODING
* DEFAULT_ENCODING})</p>
*/
public String toString();
/**
* <p>Return the {@link String} representation of this instance given
* a specific character encoding.</p>
*
* @throws UnsupportedEncodingException if the specified encoding is not
* supported by the platform.
*/
public String toString(String encoding)
throws UnsupportedEncodingException;
}

View File

@ -1,37 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.encoding;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
/**
* <p>The {@link EncodingAware} interface describes an {@link Object} aware
* of multiple encodings existing withing the platform.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public interface EncodingAware {
/** <p>The default encoding is specified as being <code>UTF-8</code>.</p> */
public static final String DEFAULT_ENCODING = "UTF-8";
/** <p>The platform encoding is evaluated at runtime from the JVM.</p> */
public static final String PLATFORM_ENCODING =
new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
}

View File

@ -1,274 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.encoding;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* <p>An utility class providing various static methods dealing with
* encodings and {@link Encodable} objects..</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public final class EncodingTools implements EncodingAware {
/** <p>The Base-64 alphabet.</p> */
private static final char ALPHABET[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' };
/** <p>Deny construction of this class.</p> */
private EncodingTools() { }
/* ====================================================================== */
/* URL ENCODING / DECODING */
/* ====================================================================== */
/**
* <p>Return the {@link String} representation of the specified
* {@link Encodable} object using the {@link EncodingAware#DEFAULT_ENCODING
* default encoding}.</p>
*
* throws NullPointerException if the {@link Encodable} was <b>null</b>.
*/
public static String toString(Encodable encodable) {
try {
return encodable.toString(DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Default encoding \"" + DEFAULT_ENCODING +
"\" not supported by the platform";
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/* ====================================================================== */
/* URL ENCODING / DECODING */
/* ====================================================================== */
/**
* <p>URL-encode the specified string.</p>
*/
public static String urlEncode(String source, String encoding)
throws UnsupportedEncodingException {
if (source == null) return null;
if (encoding == null) encoding = DEFAULT_ENCODING;
return URLEncoder.encode(source, encoding);
}
/**
* <p>URL-encode the specified string.</p>
*/
public static String urlEncode(String source) {
if (source == null) return null;
try {
return URLEncoder.encode(source, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>URL-decode the specified string.</p>
*/
public static String urlDecode(String source, String encoding)
throws UnsupportedEncodingException {
if (source == null) return null;
if (encoding == null) encoding = DEFAULT_ENCODING;
return URLDecoder.decode(source, encoding);
}
/**
* <p>URL-decode the specified string.</p>
*/
public static String urlDecode(String source) {
if (source == null) return null;
try {
return URLDecoder.decode(source, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/* ====================================================================== */
/* BASE 64 ENCODING / DECODING */
/* ====================================================================== */
/**
* <p>Encode the specified string in base 64 using the specified
* encoding.</p>
*/
public static final String base64Encode(String string, String encoding)
throws UnsupportedEncodingException {
/* Check the source string for null or the empty string. */
if (string == null) return (null);
if (string.length() == 0) return "";
/* Check the encoding */
if (encoding == null) encoding = DEFAULT_ENCODING;
/* Prepare the buffers that we'll use to encode in Base 64 */
final byte bsrc[] = string.getBytes(encoding);
final char bdst[] = new char[(bsrc.length + 2) / 3 * 4];
/* Iterate into the source in chunks of three bytes */
int psrc = -1;
int pdst = 0;
int temp = 0;
while ((psrc = psrc + 3) < bsrc.length) {
/* For every three bytes processed ... */
temp = ((bsrc[psrc - 2] << 16) & 0xFF0000) |
((bsrc[psrc - 1] << 8) & 0x00FF00) |
((bsrc[psrc ] ) & 0x0000FF);
/* ... we append four bytes to the buffer */
bdst[pdst ++] = ALPHABET[(temp >> 18) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp >> 12) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp >> 6) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp ) & 0x3f];
}
/* Let's check whether we still have some bytes to encode */
switch (psrc - bsrc.length) {
case 0: /* Two bytes left to encode */
temp = ((bsrc[psrc - 2] & 0xFF) << 8) | (bsrc[psrc - 1] & 0xFF);
bdst[pdst ++] = ALPHABET[(temp >> 10) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp >> 4) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp << 2) & 0x3c];
bdst[pdst ++] = ALPHABET[64];
break;
case 1: /* One byte left to encode */
temp = (bsrc[psrc - 2] & 0xFF);
bdst[pdst ++] = ALPHABET[(temp >> 2) & 0x3f];
bdst[pdst ++] = ALPHABET[(temp << 4) & 0x30];
bdst[pdst ++] = ALPHABET[64];
bdst[pdst ++] = ALPHABET[64];
}
/* Convert the character array into a proper string */
return new String(bdst);
}
/**
* <p>Encode the specified string in base 64 using the default encoding.</p>
*/
public static final String base64Encode(String string) {
try {
return (base64Encode(string, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Decode the specified base 64 string using the specified encoding.</p>
*/
public static final String base64Decode(String string, String encoding)
throws UnsupportedEncodingException {
/* Check the source string for null or the empty string. */
if (string == null) return (null);
if (string.length() == 0) return "";
/* Check the encoding */
if (encoding == null) encoding = DEFAULT_ENCODING;
/* Retrieve the array of characters of the source string. */
final char characters[] = string.toCharArray();
/* Check the length, which must be dividible by 4. */
if ((characters.length & 0x03) != 0)
throw new IllegalArgumentException("Invalid length for the "+
"encoded string (" + characters.length + ")");
/* The bytes array length is 3/4th of the characters array length */
byte bytes[] = new byte[characters.length - (characters.length >> 2)];
/*
* Since this might take a while check now for the last 4 characters
* token: it must contain at most two == and those need to be in the
* last two positions in the array (the only valid sequences are:
* "????", "???=" and "??==").
*/
if (((characters[characters.length - 4] == '=') ||
(characters[characters.length - 3] == '=')) ||
((characters[characters.length - 2] == '=') &&
(characters[characters.length - 1] != '='))) {
throw new IllegalArgumentException("Invalid pattern for last " +
"Base64 token in string to decode: " +
characters[characters.length - 4] +
characters[characters.length - 3] +
characters[characters.length - 2] +
characters[characters.length - 1]);
}
/* Translate the Base64-encoded String in chunks of 4 characters. */
int coff = 0;
int boff = 0;
while (coff < characters.length) {
boolean last = (coff == (characters.length - 4));
int curr = ((value(characters[coff ], last) << 0x12) |
(value(characters[coff + 1], last) << 0x0c) |
(value(characters[coff + 2], last) << 0x06) |
(value(characters[coff + 3], last) ));
bytes[boff + 2] = (byte)((curr ) & 0xff);
bytes[boff + 1] = (byte)((curr >> 0x08) & 0xff);
bytes[boff ] = (byte)((curr >> 0x10) & 0xff);
coff += 4;
boff += 3;
}
/* Get the real decoded string length, checking out the trailing '=' */
if (characters[coff - 1] == '=') boff--;
if (characters[coff - 2] == '=') boff--;
/* All done */
return (new String(bytes, 0, boff, encoding));
}
/**
* <p>Decode the specified base 64 string using the default encoding.</p>
*/
public static final String base64Decode(String string) {
try {
return (base64Decode(string, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/* ====================================================================== */
/** <p>Retrieve the offset of a character in the base 64 alphabet.</p> */
private static final int value(char character, boolean last) {
for (int x = 0; x < 64; x++) if (ALPHABET[x] == character) return (x);
if (last && (character == ALPHABET[65])) return(0);
final String message = "Character \"" + character + "\" invalid";
throw new IllegalArgumentException(message);
}
}

View File

@ -1,61 +0,0 @@
<html>
<head>
<title>Encoding Utilities</title>
</head>
<body>
<p>
This package contains a number of utility classes dealing with generic
encoding of {@link java.lang.String}s.
</p>
<p>
Although this might sound useless at first (as {@link java.lang.String}s
do support encoding internally already), this class deals with a very
subtle problem encountered when merging Java {@link java.lang.String}s
and old byte-based (non internationalized) transports, such as
Base 64 and URL encoding.
</p>
<p>
Let's consider (as an example) the URL encoded {@link java.lang.String}
<code>%C2%A3 100</code> can be easily decomposed in a byte array using
URL decoding techniques: we would end up with the following byte array:
<code>0x0C2 0x0A3 0x20 0x31 0x30 0x30</code>.
</p>
<p>
This byte-array, though, doesn't tell us anything about how to represent
this as a readable and usable {@link java.lang.String} in Java. To be
able to convert this we have to decode it again using a charset (or an
encoding).
</p>
<p>
So, for example, if we were to decode the above mentioned byte array using
the <b>ISO-8859-1</b> encoding, we would obtain the string
&quot;<code>&Acirc;&pound; 100</code>&quot;, or in details:
</p>
<ul>
<li>a latin capital letter &quot;A&quot; with a circumflex accent</li>
<li>the pound sign</li>
<li>a space</li>
<li>the number 1</li>
<li>the number 0</li>
<li>the number 0</li>
</ul>
<p>
If we were to decode the same byte sequence using <b>UTF-8</b>, on the
other hand, we would obtain the (quite different) string
&quot;<code>&pound; 100</code>&quot;, or in details:
</p>
<ul>
<li>the pound sign</li>
<li>a space</li>
<li>the number 1</li>
<li>the number 0</li>
<li>the number 0</li>
</ul>
<p>
Therefore, as a conclusion, when Java {@link java.lang.String}s are
encoded using Base 64, URL encoding, or similar techiques, one always
have to remember that encoding (or decoding) must be done twice, and
this package provides a way to deal with this mechanism.
</p>
</body>
</html>

View File

@ -1,901 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.http;
import it.could.util.StreamTools;
import it.could.util.StringTools;
import it.could.util.location.Location;
import it.could.util.location.Path;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
/**
* <p>A class implementing an extremely simple WebDAV Level 1 client based on
* the {@link HttpClient}.</p>
*
* <p>Once opened this class will represent a WebDAV collection. Users of this
* class can then from an instance of this, deal with relative parent and
* children resources.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class WebDavClient {
/** <p>The WebDAV resource asociated with this instance.</p> */
private Resource resource;
/** <p>A map of children resources of this instance.</p> */
private Map children;
/**
* <p>Create a new {@link WebDavClient} instance opening the collection
* identified by the specified {@link Location}.</p>
*
* @param location the {@link Location} of the WebDAV collection to open.
* @throws IOException if an I/O or network error occurred, or if the
* {@link Location} specified does not point to a
* WebDAV collection.
* @throws NullPointerException if the {@link Location} was <b>null</b>.
*/
public WebDavClient(Location location)
throws NullPointerException, IOException {
if (location == null) throw new NullPointerException("Null location");
this.reload(location);
}
/* ====================================================================== */
/* ACTIONS */
/* ====================================================================== */
/**
* <p>Refresh this {@link WebDavClient} instance re-connecting to the remote
* collection and re-reading its properties.</p>
*
* @return this {@link WebDavClient} instance.
*/
public WebDavClient refresh()
throws IOException {
this.reload(this.resource.location);
return this;
}
/**
* <p>Fetch the contents of the specified child resource of the collection
* represented by this {@link WebDavClient} instance.</p>
*
* @see #isCollection(String)
* @return a <b>non-null</b> {@link InputStream}.
* @throws IOException if an I/O or network error occurred, or if the
* child specified represents a collection.
* @throws NullPointerException if the child was <b>null</b>.
*/
public InputStream get(String child)
throws NullPointerException, IOException {
if (child == null) throw new NullPointerException("Null child");
if (! this.isCollection(child)) {
final Location location = this.getLocation(child);
final HttpClient client = new HttpClient(location);
client.setAcceptableStatus(200).connect("GET");
return client.getResponseStream();
}
throw new IOException("Child \"" + child + "\" is a collection");
}
/**
* <p>Delete the child resource (or collection) of the collection
* represented by this {@link WebDavClient} instance.</p>
*
* @return this {@link WebDavClient} instance.
* @throws IOException if an I/O or network error occurred, or if the
* child specified represents a collection.
* @throws NullPointerException if the child was <b>null</b>.
*/
public WebDavClient delete(String child)
throws NullPointerException, IOException {
if (child == null) throw new NullPointerException("Null child");
final HttpClient client = new HttpClient(this.getLocation(child));
client.setAcceptableStatus(204).connect("DELETE").disconnect();
return this.refresh();
}
/**
* <p>Create a new collection as a child of the collection represented
* by this {@link WebDavClient} instance.</p>
*
* <p>In comparison to {@link #put(String)} and {@link #put(String, long)}
* this method will fail if the specified child already exist.</p>
*
* @see #hasChild(String)
* @return this {@link WebDavClient} instance.
* @throws IOException if an I/O or network error occurred, or if the
* child specified already exist.
* @throws NullPointerException if the child was <b>null</b>.
*/
public WebDavClient mkcol(String child)
throws NullPointerException, IOException {
if (child == null) throw new NullPointerException("Null child");
if (this.hasChild(child))
throw new IOException("Child \"" + child + "\" already exists");
final Location location = this.resource.location.resolve(child);
final HttpClient client = new HttpClient(location);
client.setAcceptableStatus(201).connect("MKCOL").disconnect();
return this.refresh();
}
/**
* <p>Create a new (or update the contents of a) child of of the collection
* represented by this {@link WebDavClient} instance.</p>
*
* <p>This method will behave exactly like the {@link #put(String, long)}
* method, but the data written to the returned {@link OutputStream} will
* be <i>buffered in memory</i> and will be transmitted to the remote
* server only when the {@link OutputStream#close()} method is called.</p>
*
* <p>If the returned {@link OutputStream} is garbage collected before the
* {@link OutputStream#close() close()} method is called, the entire
* transaction will be aborted and no connection to the remote server will
* be established.</p>
*
* <p>Use this method in extreme cases. In normal circumstances always rely
* on the {@link #put(String, long)} method.</p>
*
* @see #put(String, long)
* @return a <b>non-null</b> {@link OutputStream} instance.
* @throws NullPointerException if the child was <b>null</b>.
*/
public OutputStream put(final String child)
throws NullPointerException {
if (child == null) throw new NullPointerException("Null child");
final WebDavClient client = this;
return new ByteArrayOutputStream() {
private boolean closed = false;
public void close()
throws IOException {
if (this.closed) return;
this.flush();
OutputStream output = client.put(child, this.buf.length);
output.write(this.buf);
output.flush();
output.close();
}
protected void finalize()
throws Throwable {
this.closed = true;
super.finalize();
}
};
}
/**
* <p>Create a new (or update the contents of a) child of of the collection
* represented by this {@link WebDavClient} instance.</p>
*
* <p>If the specified child {@link #hasChild(String) already exists} on
* the remote server, it will be {@link #delete(String) deleted} before
* writing.</p>
*
* @return a <b>non-null</b> {@link OutputStream} instance.
* @throws NullPointerException if the child was <b>null</b>.
* @throws IOException if an I/O or network error occurred, or if the
* child specified already exist.
*/
public OutputStream put(String child, long length)
throws NullPointerException, IOException {
if (child == null) throw new NullPointerException("Null child");
if (this.hasChild(child)) this.delete(child);
final Location location = this.resource.location.resolve(child);
final HttpClient client = new HttpClient(location);
client.setAcceptableStatuses(new int[] { 201, 204 });
client.connect("PUT", length);
final WebDavClient webdav = this;
return new BufferedOutputStream(client.getRequestStream()) {
boolean closed = false;
public void close()
throws IOException {
if (this.closed) return;
try {
super.close();
} finally {
this.closed = true;
webdav.refresh();
}
}
protected void finalize()
throws Throwable {
try {
this.close();
} finally {
super.finalize();
}
}
};
}
/**
* <p>Open the specified child collection of the collection represented by
* this {@link WebDavClient} as a new {@link WebDavClient} instance.</p>
*
* <p>If the specified child is &quot;<code>.</code>&quot; this method
* will behave exactly like {@link #refresh()} and <i>this instance</i>
* will be returned.</p>
*
* <p>If the specified child is &quot;<code>..</code>&quot; this method
* will behave exactly like {@link #parent()}.</p>
*
* @return a <b>non-null</b> {@link WebDavClient} instance.
* @throws NullPointerException if the child was <b>null</b>.
* @throws IOException if an I/O or network error occurred, or if the
* child specified did not exist.
*/
public WebDavClient open(String child)
throws NullPointerException, IOException {
if (child == null) throw new NullPointerException("Null child");
if (".".equals(child)) return this.refresh();
if ("..".equals(child)) return this.parent();
if (resource.collection) {
Location loc = this.getLocation().resolve(this.getLocation(child));
return new WebDavClient(loc);
}
throw new IOException("Child \"" + child + "\" is not a collection");
}
/**
* <p>Open the parent collection of the collection represented by this
* {@link WebDavClient} as a new {@link WebDavClient} instance.</p>
*
* @return a <b>non-null</b> {@link WebDavClient} instance.
* @throws IOException if an I/O or network error occurred, or if the
* child specified did not exist.
*/
public WebDavClient parent()
throws IOException {
final Location location = this.resource.location.resolve("..");
return new WebDavClient(location);
}
/* ====================================================================== */
/* ACCESSOR METHODS */
/* ====================================================================== */
/**
* <p>Return an {@link Iterator} over {@link String}s for all the children
* of the collection represented by this {@link WebDavClient} instance.</p>
*/
public Iterator iterator() {
return this.children.keySet().iterator();
}
/**
* <p>Checks if the collection represented by this {@link WebDavClient}
* contains the specified child.</p>
*/
public boolean hasChild(String child) {
return this.children.containsKey(child);
}
/**
* <p>Return the {@link Location} associated with the collection
* represented by this {@link WebDavClient}.</p>
*
* <p>The returned {@link Location} can be different from the one specified
* at construction, in case the server redirected us upon connection.</p>
*/
public Location getLocation() {
return this.resource.location;
}
/**
* <p>Return the content length (in bytes) of the collection represented
* by this {@link WebDavClient} as passed to us by the WebDAV server.</p>
*/
public long getContentLength() {
return this.resource.contentLength;
}
/**
* <p>Return the content type (mime-type) of the collection represented
* by this {@link WebDavClient} as passed to us by the WebDAV server.</p>
*/
public String getContentType() {
return this.resource.contentType;
}
/**
* <p>Return the last modified {@link Date} of the collection represented
* by this {@link WebDavClient} as passed to us by the WebDAV server.</p>
*/
public Date getLastModified() {
return this.resource.lastModified;
}
/**
* <p>Return the creation {@link Date} of the collection represented
* by this {@link WebDavClient} as passed to us by the WebDAV server.</p>
*/
public Date getCreationDate() {
return this.resource.creationDate;
}
/**
* <p>Return the {@link Location} associated with the specified child of
* the collection represented by this {@link WebDavClient}.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
public Location getLocation(String child)
throws IOException {
Location location = this.getResource(child).location;
return this.resource.location.resolve(location);
}
/**
* <p>Checks if the specified child of the collection represented by this
* {@link WebDavClient} instance is a collection.</p>
*/
public boolean isCollection(String child)
throws IOException {
return this.getResource(child).collection;
}
/**
* <p>Return the content length (in bytes) associated with the specified
* child of the collection represented by this {@link WebDavClient}.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
public long getContentLength(String child)
throws IOException {
return this.getResource(child).contentLength;
}
/**
* <p>Return the content type (mime-type) associated with the specified
* child of the collection represented by this {@link WebDavClient}.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
public String getContentType(String child)
throws IOException {
return this.getResource(child).contentType;
}
/**
* <p>Return the last modified {@link Date} associated with the specified
* child of the collection represented by this {@link WebDavClient}.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
public Date getLastModified(String child)
throws IOException {
return this.getResource(child).lastModified;
}
/**
* <p>Return the creation {@link Date} associated with the specified
* child of the collection represented by this {@link WebDavClient}.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
public Date getCreationDate(String child)
throws IOException {
return this.getResource(child).creationDate;
}
/* ====================================================================== */
/* INTERNAL METHODS */
/* ====================================================================== */
/**
* <p>Return the resource associated with the specified child.</p>
*
* @throws IOException if the specified child does not exist.
* @throws NullPointerException if the specified child was <b>null</b>.
*/
private Resource getResource(String child)
throws IOException {
if (child == null) throw new NullPointerException();
final Resource resource = (Resource) this.children.get(child);
if (resource == null) throw new IOException("Not found: " + child);
return resource;
}
/**
* <p>Contact the remote WebDAV server and fetch all properties.</p>
*/
private void reload(Location location)
throws IOException {
/* Do an OPTIONS over onto the location */
location = this.options(location);
/* Do a PROPFIND to figure out the properties and the children */
final Iterator iterator = this.propfind(location).iterator();
final Map children = new HashMap();
while (iterator.hasNext()) {
final Resource resource = (Resource) iterator.next();
final Path path = resource.location.getPath();
if (path.size() == 0) {
resource.location = location.resolve(resource.location);
this.resource = resource;
} else if (path.size() == 1) {
final Path.Element element = (Path.Element) path.get(0);
if ("..".equals(element.getName())) continue;
children.put(element.toString(), resource);
}
}
/* Check if the current resource was discovered */
if (this.resource == null)
throw new IOException("Current resource not returned in PROOPFIND");
/* Don't actually allow resources to be modified */
this.children = Collections.unmodifiableMap(children);
}
/**
* <p>Contact the remote WebDAV server and do an OPTIONS lookup.</p>
*/
private Location options(Location location)
throws IOException {
/* Create the new HttpClient instance associated with the location */
final HttpClient client = new HttpClient(location);
client.setAcceptableStatus(200).connect("OPTIONS", true).disconnect();
/* Check that the remote server returned the "Dav" header */
final List davHeader = client.getResponseHeaderValues("dav");
if (davHeader == null) {
throw new IOException("Server did not respond with a DAV header");
}
/* Check if the OPTIONS request contained the DAV header */
final Iterator iterator = davHeader.iterator();
boolean foundLevel1 = false;
while (iterator.hasNext() && (! foundLevel1)) {
String value = (String) iterator.next();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreTokens()) {
if (! "1".equals(tokenizer.nextToken().trim())) continue;
foundLevel1 = true;
break;
}
}
/* Return the (possibly redirected) location or fail miserably */
if (foundLevel1) return client.getLocation();
throw new IOException("Server doesn't support DAV Level 1");
}
/**
* <p>Contact the remote WebDAV server and do a PROPFIND lookup, returning
* a {@link List} of all scavenged resources.</p>
*/
private List propfind(Location location)
throws IOException {
/* Create the new HttpClient instance associated with the location */
final HttpClient client = new HttpClient(location);
client.addRequestHeader("Depth", "1");
client.setAcceptableStatus(207).connect("PROPFIND", true);
/* Get the XML SAX Parser and parse the output of the PROPFIND */
try {
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
final SAXParser parser = factory.newSAXParser();
final String systemId = location.toString();
final InputSource source = new InputSource(systemId);
final Handler handler = new Handler(location);
source.setByteStream(client.getResponseStream());
parser.parse(source, handler);
return handler.list;
} catch (ParserConfigurationException exception) {
Exception throwable = new IOException("Error creating XML parser");
throw (IOException) throwable.initCause(exception);
} catch (SAXException exception) {
Exception throwable = new IOException("Error creating XML parser");
throw (IOException) throwable.initCause(exception);
} finally {
client.disconnect();
}
}
/* ====================================================================== */
/* INTERNAL CLASSES */
/* ====================================================================== */
/**
* <p>An internal XML {@link DefaultHandler} used to parse out the various
* details of a PROPFIND response.</p>
*/
private static final class Handler extends DefaultHandler {
/* ================================================================== */
/* PSEUDO-XPATH LOCATIONS FOR QUICK-AND-DIRTY LOCATION LOOKUP */
/* ================================================================== */
private static final String RESPONSE_PATH = "/multistatus/response";
private static final String HREF_PATH = "/multistatus/response/href";
private static final String COLLECTION_PATH =
"/multistatus/response/propstat/prop/resourcetype/collection";
private static final String GETCONTENTTYPE_PATH =
"/multistatus/response/propstat/prop/getcontenttype";
private static final String GETLASTMODIFIED_PATH =
"/multistatus/response/propstat/prop/getlastmodified";
private static final String GETCONTENTLENGTH_PATH =
"/multistatus/response/propstat/prop/getcontentlength";
private static final String CREATIONDATE_PATH =
"/multistatus/response/propstat/prop/creationdate";
/** <p>The {@link Location} for resolving all other links.</p> */
private final Location base;
/** <p>The {@link List} of all scavenged resources.</p> */
private final List list = new ArrayList();
/** <p>The resource currently being processed.</p> */
private Resource rsrc = null;
/** <p>A {@link StringBuffer} holding character data.</p> */
private StringBuffer buff = null;
/** <p>A {@link Stack} for quick-and-dirty pseudo XPath lookups.</p> */
private Stack stack = new Stack();
/**
* <p>Create a new instance specifying the base {@link Location}.</p>
*/
private Handler(Location location) {
this.base = location;
}
/**
* <p>Push an element name in the stack for pseudo-XPath lookups.</p>
*
* @return a {@link String} like <code>/element/element/element</code>.
*/
private String pushPath(String path) {
this.stack.push(path.toLowerCase());
final StringBuffer buffer = new StringBuffer();
for (int x = 0; x < this.stack.size(); x ++)
buffer.append('/').append(this.stack.get(x));
return buffer.toString();
}
/**
* <p>Pop the last element name from the pseudo-XPath lookup stack.</p>
*
* @return a {@link String} like <code>/element/element/element</code>.
*/
private String popPath(String path)
throws SAXException {
final StringBuffer buffer = new StringBuffer();
final String last = (String) this.stack.pop();
if (path.toLowerCase().equals(last)) {
for (int x = 0; x < this.stack.size(); x ++)
buffer.append('/').append(this.stack.get(x));
return buffer.append('/').append(last).toString();
}
throw new SAXException("Tag <" + path + "/> unbalanced at path \""
+ pushPath(last) + "\"");
}
/**
* <p>Handle the start-of-element SAX event.</p>
*/
public void startElement(String uri, String l, String q, Attributes a)
throws SAXException {
if (! "DAV:".equals(uri.toUpperCase())) return;
final String path = this.pushPath(l);
if (RESPONSE_PATH.equals(path)) {
this.rsrc = new Resource();
} else if (COLLECTION_PATH.equals(path)) {
if (this.rsrc != null) this.rsrc.collection = true;
} else if (GETCONTENTTYPE_PATH.equals(path) ||
GETLASTMODIFIED_PATH.equals(path) ||
GETCONTENTLENGTH_PATH.equals(path) ||
CREATIONDATE_PATH.equals(path) ||
HREF_PATH.equals(path)) {
this.buff = new StringBuffer();
}
}
/**
* <p>Handle the end-of-element SAX event.</p>
*/
public void endElement(String uri, String l, String q)
throws SAXException {
if (! "DAV:".equals(uri.toUpperCase())) return;
final String path = this.popPath(l);
final String data = this.resetBuffer();
if (RESPONSE_PATH.equals(path)) {
if (this.rsrc != null) {
if (this.rsrc.location != null) {
if (this.rsrc.location.isAbsolute()) {
final String z = this.rsrc.location.toString();
throw new SAXException("Unresolved location " + z);
} else {
this.list.add(this.rsrc);
}
} else {
throw new SAXException("Null location for resource");
}
}
} else if (HREF_PATH.equals(path)) {
if (this.rsrc != null) try {
final Location resolved = this.base.resolve(data);
this.rsrc.location = this.base.relativize(resolved);
if (! this.rsrc.location.isRelative())
throw new SAXException("Unable to relativize location "
+ this.rsrc.location);
} catch (MalformedURLException exception) {
final String msg = "Unable to resolve URL \"" + data + "\"";
SAXException throwable = new SAXException(msg, exception);
throw (SAXException) throwable.initCause(exception);
}
} else if (CREATIONDATE_PATH.equals(path)) {
if (this.rsrc != null)
this.rsrc.creationDate = StringTools.parseIsoDate(data);
} else if (GETCONTENTTYPE_PATH.equals(path)) {
if (this.rsrc != null) this.rsrc.contentType = data;
} else if (GETLASTMODIFIED_PATH.equals(path)) {
if (this.rsrc != null)
this.rsrc.lastModified = StringTools.parseHttpDate(data);
} else if (GETCONTENTLENGTH_PATH.equals(path)) {
if (this.rsrc != null) {
Long length = StringTools.parseNumber(data);
if (length != null) {
this.rsrc.contentLength = length.longValue();
}
}
}
}
/**
* <p>Handle SAX characters notification.</p>
*/
public void characters(char buffer[], int offset, int length) {
if (this.buff != null) this.buff.append(buffer, offset, length);
}
/**
* <p>Reset the current characters buffer and return it as a
* {@link String}.</p>
*/
private String resetBuffer() {
if (this.buff == null) return null;
if (this.buff.length() == 0) {
this.buff = null;
return null;
}
final String value = this.buff.toString();
this.buff = null;
return value;
}
}
/**
* <p>A simple class holding the core resource properties.</p>
*/
private static class Resource {
private Location location = null;
private boolean collection = false;
private long contentLength = -1;
private String contentType = null;
private Date lastModified = null;
private Date creationDate = null;
}
/* ====================================================================== */
/* COMMAND LINE CLIENT */
/* ====================================================================== */
/**
* <p>A command-line interface to a WebDAV repository.</p>
*
* <p>When invoked from the command line, this class requires one only
* argument, the URL location of the WebDAV repository to connect to.</p>
*
* <p>After connection this method will interact with the user using an
* extremely simple console-based interface.</p>
*/
public static void main(String args[])
throws IOException {
final InputStreamReader r = new InputStreamReader(System.in);
final BufferedReader in = new BufferedReader(r);
WebDavClient client = new WebDavClient(Location.parse(args[0]));
while (true) try {
System.out.print("[" + client.getLocation() + "] -> ");
args = parse(in.readLine());
if (args == null) break;
if (args[0].equals("list")) {
if (args[1] == null) list(client, System.out);
else list(client.open(args[1]), System.out);
} else if (args[0].equals("refresh")) {
client = client.refresh();
} else if (args[0].equals("get")) {
if (args[1] != null) {
final InputStream input = client.get(args[1]);
final File file = new File(args[2]).getCanonicalFile();
final OutputStream output = new FileOutputStream(file);
final long bytes = StreamTools.copy(input, output);
System.out.println("Fetched child \"" + args[1] +
"\" to file \"" + file + "\" (" +
bytes + " bytes)");
}
else System.out.print("Can't \"get\" null");
} else if (args[0].equals("put")) {
if (args[1] != null) {
final File file = new File(args[1]).getCanonicalFile();
final InputStream input = new FileInputStream(file);
final OutputStream output = client.put(args[2], file.length());
final long bytes = StreamTools.copy(input, output);
System.out.println("Uploaded file \"" + file +
"\" to child \"" + args[2] + "\" (" +
bytes + " bytes)");
}
else System.out.print("Can't \"put\" null");
} else if (args[0].equals("mkcol")) {
if (args[1] != null) {
client.mkcol(args[1]);
System.out.println("Created \"" + args[1] + "\"");
}
else System.out.print("Can't \"mkcol\" null");
} else if (args[0].equals("delete")) {
if (args[1] != null) {
client.delete(args[1]);
System.out.println("Deleted \"" + args[1] + "\"");
}
else System.out.print("Can't \"delete\" null");
} else if (args[0].equals("cd")) {
if (args[1] != null) client = client.open(args[1]);
else System.out.print("Can't \"cd\" to null");
} else if (args[0].equals("quit")) {
break;
} else {
System.out.print("Invalid command \"" + args[0] + "\". ");
System.out.println("Valid commands are:");
System.out.println(" - \"list\" list the children child");
System.out.println(" - \"get\" fetch the specified child");
System.out.println(" - \"put\" put the specified child");
System.out.println(" - \"mkcol\" create a collection");
System.out.println(" - \"delete\" delete a child");
System.out.println(" - \"put\" put the specified resource");
System.out.println(" - \"cd\" change the location");
System.out.println(" - \"refresh\" refresh this location");
System.out.println(" - \"quit\" quit this application");
}
} catch (Exception exception) {
exception.printStackTrace(System.err);
}
System.err.println();
}
/**
* <p>Parse a line entered by the user returning a three-tokens argument
* list (command, argument 1, argument 2)</p>
*/
private static String[] parse(String line) {
if (line == null) return null;
final String array[] = new String[3];
final StringTokenizer tokenizer = new StringTokenizer(line);
int offset = 0;
while (tokenizer.hasMoreTokens() && (offset < 3))
array[offset ++] = tokenizer.nextToken();
if (array[0] == null) return null;
if (array[2] == null) array[2] = array[1];
return array;
}
/**
* <p>Pseudo-nicely display a list of the children of a collection</p>
*/
private static void list(WebDavClient client, PrintStream out)
throws IOException {
out.print("C | ");
out.print("CONTENT TYPE | ");
out.print("CREATED | ");
out.print("MODIFIED | ");
out.print("SIZE | ");
out.println("NAME ");
for (Iterator iterator = client.iterator(); iterator.hasNext() ; ) {
final StringBuffer buffer = new StringBuffer();
String child = (String) iterator.next();
if (client.isCollection(child)) buffer.append("* | ");
else buffer.append(" | ");
format(buffer, client.getContentType(child), 15).append(" | ");
format(buffer, client.getCreationDate(child), 19).append(" | ");
format(buffer, client.getLastModified(child), 19).append(" | ");
format(buffer, client.getContentLength(child), 10).append(" | ");
out.println(buffer.append(child));
}
}
/** <p>Format a number aligning it to the right of a string.</p> */
private static StringBuffer format(StringBuffer buf, long num, int len) {
final String data;
if (num < 0) data = "";
else data = Long.toString(num);
final int spaces = len - data.length();
for (int x = 0; x < spaces; x++) buf.append(' ');
buf.append(data);
return buf;
}
/** <p>Format a string into an exact number of characters.</p> */
private static StringBuffer format(StringBuffer buf, Object obj, int len) {
final String string;
if (obj == null) {
string = ("[null]");
} else if (obj instanceof Date) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
string = f.format((Date) obj);
} else {
string = obj.toString();
}
final StringBuffer buffer = new StringBuffer(string);
for (int x = string.length(); x < len; x ++) buffer.append(' ');
return buf.append(buffer.substring(0, len));
}
}

View File

@ -1,12 +0,0 @@
<html>
<head>
<title>HTTP Utilities</title>
</head>
<body>
<p>
This package contains a number of utility classes to access
<a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a> and
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a> servers.
</p>
</body>
</html>

View File

@ -1,805 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.location;
import it.could.util.StringTools;
import it.could.util.encoding.Encodable;
import it.could.util.encoding.EncodingTools;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>An utility class representing an HTTP-like URL.</p>
*
* <p>This class can be used to represent any URL that roughly uses the HTTP
* format. Compared to the standard {@link java.net.URL} class, the scheme part
* of the a {@link Location} is never checked, and it's up to the application
* to verify its correctness, while compared to the {@link java.net.URI} class,
* its parsing mechanism is a lot more relaxed (be liberal in what you accept,
* be strict in what you send).</p>
*
* <p>For a bigger picture on how this class works, this is an easy-to-read
* representation of what the different parts of a {@link Location} are:</p>
*
* <div align="center">
* <a href="url.pdf" target="_new" title="PDF Version">
* <img src="url.gif" alt="URL components" border="0">
* </a>
* </div>
*
* <p>One important difference between this implementation and the description
* of <a href="http://www.ietf.org/rfc/rfc1738.txt">URLs</a> and
* <a href="http://www.ietf.org/rfc/rfc2396.txt">URIs</a> is that parameter
* paths are represented <i>only at the end of the entire path structure</i>
* rather than for each path element. This over-simplification allows easy
* relativization of {@link Location}s when used with servlet containers, which
* normally use path parameters to encode the session id.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class Location implements Encodable {
/** <p>A {@link Map} of schemes and their default port number.</p> */
private static final Map schemePorts = new HashMap();
static {
schemePorts.put("acap", new Integer( 674));
schemePorts.put("dav", new Integer( 80));
schemePorts.put("ftp", new Integer( 21));
schemePorts.put("gopher", new Integer( 70));
schemePorts.put("http", new Integer( 80));
schemePorts.put("https", new Integer( 443));
schemePorts.put("imap", new Integer( 143));
schemePorts.put("ldap", new Integer( 389));
schemePorts.put("mailto", new Integer( 25));
schemePorts.put("news", new Integer( 119));
schemePorts.put("nntp", new Integer( 119));
schemePorts.put("pop", new Integer( 110));
schemePorts.put("rtsp", new Integer( 554));
schemePorts.put("sip", new Integer(5060));
schemePorts.put("sips", new Integer(5061));
schemePorts.put("snmp", new Integer( 161));
schemePorts.put("telnet", new Integer( 23));
schemePorts.put("tftp", new Integer( 69));
}
/** <p>The {@link List} of schemes of this {@link Location}.</p> */
private final Schemes schemes;
/** <p>The {@link Authority} of this {@link Location}.</p> */
private final Authority authority;
/** <p>The {@link Path} of this {@link Location}.</p> */
private final Path path;
/** <p>The {@link Parameters} of this {@link Location}.</p> */
private final Parameters parameters;
/** <p>The fragment part of this {@link Location}.</p> */
private final String fragment;
/** <p>The string representation of this {@link Location}.</p> */
private final String string;
/**
* <p>Create a new {@link Location} instance.</p>
*/
public Location(Schemes schemes, Authority authority, Path path,
Parameters parameters, String fragment)
throws MalformedURLException {
if ((schemes == null) && (authority != null))
throw new MalformedURLException("No schemes specified");
if ((schemes != null) && (authority == null))
throw new MalformedURLException("No authority specified");
if (path == null) throw new MalformedURLException("No path specified");
this.schemes = schemes;
this.authority = authority;
this.path = path;
this.parameters = parameters;
this.fragment = fragment;
this.string = EncodingTools.toString(this);
}
/* ====================================================================== */
/* STATIC CONSTRUCTION METHODS */
/* ====================================================================== */
public static Location parse(String url)
throws MalformedURLException {
try {
return parse(url, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
public static Location parse(String url, String encoding)
throws MalformedURLException, UnsupportedEncodingException {
if (url == null) return null;;
if (encoding == null) encoding = DEFAULT_ENCODING;
final String components[] = parseComponents(url);
final Schemes schemes = parseSchemes(components[0], encoding);
final int port = findPort(schemes, encoding);
final Authority auth = parseAuthority(components[1], port, encoding);
final Path path = Path.parse(components[2], encoding);
final Parameters params = Parameters.parse(components[3], '&', encoding);
final String fragment = components[4];
return new Location(schemes, auth, path, params, fragment);
}
/* ====================================================================== */
/* ACCESSOR METHODS */
/* ====================================================================== */
/**
* <p>Return an unmodifiable {@link Schemes list of all schemes} for this
* {@link Location} instance or <b>null</b>.</p>
*/
public Schemes getSchemes() {
return this.schemes;
}
/**
* <p>Return the {@link Location.Authority Authority} part for this
* {@link Location} or <b>null</b>.</p>
*/
public Authority getAuthority() {
return this.authority;
}
/**
* <p>Return the <b>non-null</b> {@link Path Path} structure
* associated with this {@link Location} instance.</p>
*/
public Path getPath() {
return this.path;
}
/**
* <p>Return an unmodifiable {@link Parameters list of all parameters}
* parsed from this {@link Location}'s query string or <b>null</b>.</p>
*/
public Parameters getParameters() {
return this.parameters;
}
/**
* <p>Return the fragment of this {@link Location} unencoded.</p>
*/
public String getFragment() {
return this.fragment;
}
/* ====================================================================== */
/* OBJECT METHODS */
/* ====================================================================== */
/**
* <p>Check if the specified {@link Object} is equal to this instance.</p>
*
* <p>The specified {@link Object} must be a <b>non-null</b>
* {@link Location} instance whose {@link #toString() string value} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Location)) {
return this.string.equals(((Location)object).string);
} else {
return false;
}
}
/**
* <p>Return the hash code value for this {@link Location} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Return the {@link String} representation of this {@link Location}
* instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the {@link String} representation of this {@link Location}
* instance using the specified character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
final StringBuffer buffer = new StringBuffer();
/* Render the schemes */
if (this.schemes != null)
buffer.append(this.schemes.toString(encoding)).append("://");
/* Render the authority part */
if (this.authority != null)
buffer.append(this.authority.toString(encoding));
/* Render the paths */
buffer.append(this.path.toString(encoding));
/* Render the query string */
if (this.parameters != null)
buffer.append('?').append(this.parameters.toString(encoding));
/* Render the fragment */
if (this.fragment != null) {
buffer.append('#');
buffer.append(EncodingTools.urlEncode(this.fragment, encoding));
}
/* Return the string */
return buffer.toString();
}
/* ====================================================================== */
/* PUBLIC METHODS */
/* ====================================================================== */
/**
* <p>Checks whether this {@link Location} is absolute or not.</p>
*
* <p>This method must not be confused with the similarly named
* {@link Path#isAbsolute() Path.isAbsolute()} method.
* This method will check whether the full {@link Location} is absolute (it
* has a scheme), while the one exposed by the {@link Path Path}
* class will check if the path is absolute.</p>
*/
public boolean isAbsolute() {
return this.schemes != null && this.authority != null;
}
public boolean isRelative() {
return ! (this.isAbsolute() || this.path.isAbsolute());
}
public boolean isAuthoritative(Location location) {
if (! this.isAbsolute()) return false;
if (! location.isAbsolute()) return true;
return this.schemes.equals(location.schemes) &&
this.authority.equals(location.authority);
}
/* ====================================================================== */
/* RESOLUTION METHODS */
/* ====================================================================== */
public Location resolve(String url)
throws MalformedURLException {
try {
return this.resolve(parse(url, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
public Location resolve(String url, String encoding)
throws MalformedURLException, UnsupportedEncodingException {
if (encoding == null) encoding = DEFAULT_ENCODING;
return this.resolve(parse(url, encoding));
}
public Location resolve(Location location) {
if (! this.isAuthoritative(location)) return location;
/* Schemes are the same */
final Schemes schemes = this.schemes;
/* Authority needs to be merged (for username and password) */
final Authority auth;
if (location.authority != null) {
final String username = location.authority.username != null ?
location.authority.username :
this.authority.username;
final String password = location.authority.password != null ?
location.authority.password :
this.authority.password;
final String host = location.authority.host;
final int port = location.authority.port;
auth = new Authority(username, password, host, port);
} else {
auth = this.authority;
}
/* Path can be resolved */
final Path path = this.path.resolve(location.path);
/* Parametrs and fragment are the ones of the target */
final Parameters params = location.parameters;
final String fragment = location.fragment;
/* Create a new {@link Location} instance */
try {
return new Location(schemes, auth, path, params, fragment);
} catch (MalformedURLException exception) {
/* Should really never happen */
Error error = new InternalError("Can't instantiate Location");
throw (Error) error.initCause(exception);
}
}
/* ====================================================================== */
/* RELATIVIZATION METHODS */
/* ====================================================================== */
public Location relativize(String url)
throws MalformedURLException {
try {
return this.relativize(parse(url, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
public Location relativize(String url, String encoding)
throws MalformedURLException, UnsupportedEncodingException {
if (encoding == null) encoding = DEFAULT_ENCODING;
return this.relativize(parse(url, encoding));
}
public Location relativize(Location location) {
final Path path;
if (!location.isAbsolute()) {
/* Target location is not absolute, its path might */
path = this.path.relativize(location.path);
} else {
if (this.isAuthoritative(location)) {
/* Target location is not on the same authority, process path */
path = this.path.relativize(location.path);
} else {
/* Not authoritative for a non-relative location, yah! */
return location;
}
}
try {
return new Location(null, null, path, location.parameters,
location.fragment);
} catch (MalformedURLException exception) {
/* Should really never happen */
Error error = new InternalError("Can't instantiate Location");
throw (Error) error.initCause(exception);
}
}
/* ====================================================================== */
/* INTERNAL PARSING ROUTINES */
/* ====================================================================== */
/**
* <p>Return the port number associated with the specified schemes.</p>
*/
public static int findPort(List schemes, String encoding)
throws UnsupportedEncodingException {
if (schemes == null) return -1;
if (schemes.size() < 1) return -1;
Integer p = (Integer) schemePorts.get(schemes.get(schemes.size() - 1));
return p == null ? -1 : p.intValue();
}
/**
* <p>Parse <code>scheme://authority/path?query#fragment</code>.</p>
*
* @return an array of five {@link String}s: scheme (0), authority (1),
* path (2), query (3) and fragment (4).
*/
private static String[] parseComponents(String url)
throws MalformedURLException {
/* Scheme, easy and simple */
final String scheme;
final String afterScheme;
final int schemeEnd = url.indexOf(":/");
if (schemeEnd > 0) {
scheme = url.substring(0, schemeEnd).toLowerCase();
afterScheme = url.substring(schemeEnd + 2);
} else if (schemeEnd == 0) {
throw new MalformedURLException("Missing scheme");
} else {
scheme = null;
afterScheme = url;
}
/* Authority (can be tricky because it can be emtpy) */
final String auth;
final String afterAuth;
if (scheme == null) {
// --> /path... or path...
afterAuth = afterScheme;
auth = null;
} else if (afterScheme.length() > 0 && afterScheme.charAt(0) == '/') {
// --> scheme://...
final int pathStart = afterScheme.indexOf('/', 1);
if (pathStart == 1) {
// --> scheme:///path...
afterAuth = afterScheme.substring(pathStart);
auth = null;
} else if (pathStart > 1) {
// --> scheme://authority/path...
afterAuth = afterScheme.substring(pathStart);
auth = afterScheme.substring(1, pathStart);
} else {
// --> scheme://authority (but no slashes for the path)
final int authEnds = StringTools.findFirst(afterScheme, "?#");
if (authEnds < 0) {
// --> scheme://authority (that's it, return)
auth = afterScheme.substring(1);
return new String[] { scheme, auth, "/", null, null };
}
// --> scheme://authority?... or scheme://authority#...
auth = afterScheme.substring(1, authEnds);
afterAuth = "/" + afterScheme.substring(authEnds);
}
} else {
// --> scheme:/path...
afterAuth = url.substring(schemeEnd + 1);
auth = null;
}
/* Path, can be terminated by '?' or '#' whichever is first */
final int pathEnds = StringTools.findFirst(afterAuth, "?#");
if (pathEnds < 0) {
// --> ...path... (no fragment or query, return now)
return new String[] { scheme, auth, afterAuth, null, null };
}
/* We have either a query, a fragment or both after the path */
final String path = afterAuth.substring(0, pathEnds);
final String afterPath = afterAuth.substring(pathEnds + 1);
/* Query? The query can contain a "#" and has an extra fragment */
if (afterAuth.charAt(pathEnds) == '?') {
final int fragmPos = afterPath.indexOf('#');
if (fragmPos < 0) {
// --> ...path...?... (no fragment)
return new String[] { scheme, auth, path, afterPath, null };
}
// --> ...path...?...#... (has also a fragment)
final String query = afterPath.substring(1, fragmPos);
final String fragm = afterPath.substring(fragmPos + 1);
return new String[] { scheme, auth, path, query, fragm };
}
// --> ...path...#... (a path followed by a fragment but no query)
return new String[] { scheme, auth, path, null, afterPath };
}
/**
* <p>Parse <code>scheme:scheme:scheme...</code>.</p>
*/
private static Schemes parseSchemes(String scheme, String encoding)
throws MalformedURLException, UnsupportedEncodingException {
if (scheme == null) return null;
final String split[] = StringTools.splitAll(scheme, ':');
List list = new ArrayList();
for (int x = 0; x < split.length; x++) {
if (split[x] == null) continue;
list.add(EncodingTools.urlDecode(split[x], encoding));
}
if (list.size() != 0) return new Schemes(list);
throw new MalformedURLException("Empty scheme detected");
}
/**
* <p>Parse <code>username:password@hostname:port</code>.</p>
*/
private static Authority parseAuthority(String auth, int defaultPort,
String encoding)
throws MalformedURLException, UnsupportedEncodingException {
if (auth == null) return null;
final String split[] = StringTools.splitOnce(auth, '@', true);
final String uinfo[] = StringTools.splitOnce(split[0], ':', false);
final String hinfo[] = StringTools.splitOnce(split[1], ':', false);
final int port;
if ((split[0] != null) && (split[1] == null))
throw new MalformedURLException("Missing required host info part");
if ((uinfo[0] == null) && (uinfo[1] != null))
throw new MalformedURLException("Password specified without user");
if ((hinfo[0] == null) && (hinfo[1] != null))
throw new MalformedURLException("Port specified without host");
try {
if (hinfo[1] != null) {
final int parsedPort = Integer.parseInt(hinfo[1]);
if ((parsedPort < 1) || (parsedPort > 65535)) {
final String message = "Invalid port number " + parsedPort;
throw new MalformedURLException(message);
}
/* If the specified port is the default one, ignore it! */
if (defaultPort == parsedPort) port = -1;
else port = parsedPort;
} else {
port = -1;
}
} catch (NumberFormatException exception) {
throw new MalformedURLException("Specified port is not a number");
}
return new Authority(EncodingTools.urlDecode(uinfo[0], encoding),
EncodingTools.urlDecode(uinfo[1], encoding),
EncodingTools.urlDecode(hinfo[0], encoding),
port);
}
/* ====================================================================== */
/* PUBLIC INNER CLASSES */
/* ====================================================================== */
/**
* <p>The {@link Location.Schemes Schemes} class represents an unmodifiable
* ordered collection of {@link String} schemes for a {@link Location}.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public static class Schemes extends AbstractList implements Encodable {
/** <p>All the {@link String} schemes in order.</p> */
private final String schemes[];
/** <p>The {@link String} representation of this instance.</p> */
private final String string;
/**
* <p>Create a new {@link Schemes} instance.</p>
*/
private Schemes(List schemes) {
final int size = schemes.size();
this.schemes = (String []) schemes.toArray(new String[size]);
this.string = EncodingTools.toString(this);
}
/**
* <p>Return the {@link String} scheme at the specified index.</p>
*/
public Object get(int index) {
return this.schemes[index];
}
/**
* <p>Return the number of {@link String} schemes contained by this
* {@link Location.Schemes Schemes} instance.</p>
*/
public int size() {
return this.schemes.length;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Location.Schemes Schemes} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Location.Schemes Schemes} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
final StringBuffer buffer = new StringBuffer();
for (int x = 0; x < this.schemes.length; x ++) {
buffer.append(':');
buffer.append(EncodingTools.urlEncode(this.schemes[x], encoding));
}
return buffer.substring(1);
}
/**
* <p>Return the hash code value for this
* {@link Location.Schemes Schemes} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Location.Schemes Schemes} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, it is a {@link Location.Schemes Schemes}
* instance, and its {@link #toString() string representation} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Schemes)) {
return this.string.equals(((Schemes) object).string);
} else {
return false;
}
}
}
/* ====================================================================== */
/**
* <p>The {@link Location.Authority Authority} class represents the autority
* and user information for a {@link Location}.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public static class Authority implements Encodable {
/** <p>The username of this instance (decoded).</p> */
private final String username;
/** <p>The password of this instance (decoded).</p> */
private final String password;
/** <p>The host name of this instance (decoded).</p> */
private final String host;
/** <p>The port number of this instance.</p> */
private final int port;
/** <p>The encoded host and port representation.</p> */
private final String hostinfo;
/** <p>The encoded string representation of this instance.</p> */
private final String string;
/**
* <p>Create a new {@link Location.Authority Authority} instance.</p>
*/
private Authority(String user, String pass, String host, int port) {
this.username = user;
this.password = pass;
this.host = host;
this.port = port;
try {
this.hostinfo = this.getHostInfo(DEFAULT_ENCODING);
this.string = this.toString(DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Default encoding \"" + DEFAULT_ENCODING
+ "\" not supported by the platform";
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Returns the decoded user name.</p>
*/
public String getUsername() {
return this.username;
}
/**
* <p>Returns the decoded password.</p>
*/
public String getPassword() {
return this.password;
}
/**
* <p>Returns the &quot;user info&quot; field.</p>
*
* <p>This method will concatenate the username and password using the
* colon character and return a <b>non-null</b> {@link String} only if
* both of them are <b>non-null</b>.</p>
*/
public String getUserInfo() {
if ((this.username == null) || (this.password == null)) return null;
return this.username + ':' + this.password;
}
/**
* <p>Returns the decoded host name.</p>
*/
public String getHost() {
return this.host;
}
/**
* <p>Returns the port number.</p>
*/
public int getPort() {
return this.port;
}
/**
* <p>Returns the host info part of the
* {@link Location.Authority Authority}.</p>
*
* <p>This is the encoded representation of the
* {@link #getUsername() user name} optionally follwed by the colon (:)
* character and the encoded {@link #getPassword() password}.</p>
*/
public String getHostInfo() {
return this.hostinfo;
}
/**
* <p>Returns the host info part of the
* {@link Location.Authority Authority} using the specified character
* encoding.</p>
*
* <p>This is the encoded representation of the
* {@link #getUsername() user name} optionally follwed by the colon (:)
* character and the encoded {@link #getPassword() password}.</p>
*/
public String getHostInfo(String encoding)
throws UnsupportedEncodingException {
final StringBuffer hostinfo = new StringBuffer();
hostinfo.append(EncodingTools.urlEncode(this.host, encoding));
if (port >= 0) hostinfo.append(':').append(port);
return hostinfo.toString();
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Location.Authority Authority} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Location.Authority Authority} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
final StringBuffer buffer;
if (this.username != null) {
buffer = new StringBuffer();
buffer.append(EncodingTools.urlEncode(this.username, encoding));
if (this.password != null) {
buffer.append(':');
buffer.append(EncodingTools.urlEncode(this.password, encoding));
}
} else {
buffer = null;
}
if (buffer == null) return this.getHostInfo(encoding);
buffer.append('@').append(this.getHostInfo(encoding));
return buffer.toString();
}
/**
* <p>Return the hash code value for this
* {@link Location.Authority Authority} instance.</p>
*/
public int hashCode() {
return this.hostinfo.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Location.Authority Authority} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, it is a {@link Location.Authority Authority}
* instance, and its {@link #getHostInfo() host info} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Authority)) {
return this.hostinfo.equals(((Authority) object).hostinfo);
} else {
return false;
}
}
}
}

View File

@ -1,474 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.location;
import it.could.util.StringTools;
import it.could.util.encoding.Encodable;
import it.could.util.encoding.EncodingTools;
import java.io.UnsupportedEncodingException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>The {@link Parameters Parameters} class represents a never empty and
* immutable {@link List} of {@link Parameters.Parameter Parameter} instances,
* normally created parsing a query string.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class Parameters extends AbstractList implements Encodable {
/** <p>The default delimiter for a {@link Parameters} instance.</p> */
public static final char DEFAULT_DELIMITER = '&';
/** <p>All the {@link Parameter}s in order.</p> */
private final Parameter parameters[];
/** <p>The {@link Map} view over all parameters (names are keys).</p> */
private final Map map;
/** <p>The {@link Set} of all parameter names.</p> */
final Set names;
/** <p>The character delimiting different parameters.</p> */
private final char delimiter;
/** <p>The encoded {@link String} representation of this.</p> */
private final String string;
/**
* <p>Create a new {@link Parameters Parameters} instance from
* a {@link List} of {@link Parameters.Parameter Parameter} instances
* using the {@link #DEFAULT_DELIMITER default parameter delimiter}.</p>
*
* @throws NullPointerExceptoin if the {@link List} was <b>null</b>.
* @throws IllegalArgumentException if the {@link List} was empty.
* @throws ClassCastException if any of the elements in the {@link List} was
* not a {@link Parameters.Parameter Parameter}.
*/
public Parameters(List parameters) {
this(parameters, DEFAULT_DELIMITER);
}
/**
* <p>Create a new {@link Parameters Parameters} instance from
* a {@link List} of {@link Parameters.Parameter Parameter} instances
* using the specified character as the parameters delimiter.</p>
*
* @throws NullPointerExceptoin if the {@link List} was <b>null</b>.
* @throws IllegalArgumentException if the {@link List} was empty.
* @throws ClassCastException if any of the elements in the {@link List} was
* not a {@link Parameters.Parameter Parameter}.
*/
public Parameters(List parameters, char delimiter) {
if (parameters.size() == 0) throw new IllegalArgumentException();
final Parameter array[] = new Parameter[parameters.size()];
final Map map = new HashMap();
for (int x = 0; x < array.length; x ++) {
final Parameter parameter = (Parameter) parameters.get(x);
final String key = parameter.getName();
List values = (List) map.get(key);
if (values == null) {
values = new ArrayList();
map.put(key, values);
}
values.add(parameter.getValue());
array[x] = parameter;
}
/* Make all parameter value lists unmodifiable */
for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
final Map.Entry entry = (Map.Entry) iter.next();
final List list = (List) entry.getValue();
entry.setValue(Collections.unmodifiableList(list));
}
/* Store the current values */
this.delimiter = delimiter;
this.map = Collections.unmodifiableMap(map);
this.names = Collections.unmodifiableSet(map.keySet());
this.parameters = array;
this.string = EncodingTools.toString(this);
}
/* ====================================================================== */
/* STATIC CONSTRUCTION METHODS */
/* ====================================================================== */
/**
* <p>Utility method to create a new {@link Parameters} instance from a
* {@link List} of {@link Parameters.Parameter Parameter} instances.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified {@link List} was <b>null</b>, empty
* or did not contain any {@link Parameters.Parameter Parameter}.
* @throws ClassCastException if any of the elements in the {@link List} was
* not a {@link Parameters.Parameter Parameter}.
*/
public static Parameters create(List parameters) {
return create(parameters, DEFAULT_DELIMITER);
}
/**
* <p>Utility method to create a new {@link Parameters} instance from a
* {@link List} of {@link Parameters.Parameter Parameter} instances.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified {@link List} was <b>null</b>, empty
* or did not contain any {@link Parameters.Parameter Parameter}.
* @throws ClassCastException if any of the elements in the {@link List} was
* not a {@link Parameters.Parameter Parameter}.
*/
public static Parameters create(List parameters, char delimiter) {
if (parameters == null) return null;
final List dedupes = new ArrayList();
for (Iterator iter = parameters.iterator(); iter.hasNext(); ) {
Object next = iter.next();
if (dedupes.contains(next)) continue;
dedupes.add(next);
}
if (dedupes.size() == 0) return null;
return new Parameters(dedupes, delimiter);
}
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters} instance using the {@link #DEFAULT_DELIMITER default
* parameter delimiter}.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified string was <b>null</b>, empty or
* did not contain any {@link Parameters.Parameter Parameter}.
*/
public static Parameters parse(String parameters) {
try {
return parse(parameters, DEFAULT_DELIMITER, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters} instance using the specified character as the
* parameters delimiter.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified string was <b>null</b>, empty or
* did not contain any {@link Parameters.Parameter Parameter}.
*/
public static Parameters parse(String parameters, char delimiter) {
try {
return parse(parameters, delimiter, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters} instance using the {@link #DEFAULT_DELIMITER default
* parameter delimiter}.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified string was <b>null</b>, empty or
* did not contain any {@link Parameters.Parameter Parameter}.
*/
public static Parameters parse(String parameters, String encoding)
throws UnsupportedEncodingException {
return parse(parameters, DEFAULT_DELIMITER, encoding);
}
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters} instance using the specified character as the
* parameters delimiter.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters} instance or
* <b>null</b> if the specified string was <b>null</b>, empty or
* did not contain any {@link Parameters.Parameter Parameter}.
*/
public static Parameters parse(String parameters, char delimiter,
String encoding)
throws UnsupportedEncodingException {
if (parameters == null) return null;
if (parameters.length() == 0) return null;
if (encoding == null) encoding = DEFAULT_ENCODING;
final String split[] = StringTools.splitAll(parameters, delimiter);
final List list = new ArrayList();
for (int x = 0; x < split.length; x ++) {
if (split[x] == null) continue;
if (split[x].length() == 0) continue;
Parameter parameter = Parameter.parse(split[x], encoding);
if (parameter != null) list.add(parameter);
}
if (list.size() == 0) return null;
return new Parameters(list, delimiter);
}
/* ====================================================================== */
/* PUBLIC EXPOSED METHODS */
/* ====================================================================== */
/**
* <p>Return the number of {@link Parameters.Parameter Parameter}s
* contained by this instance.</p>
*/
public int size() {
return this.parameters.length;
}
/**
* <p>Return the {@link Parameters.Parameter Parameter} stored by this\
* instance at the specified index.</p>
*/
public Object get(int index) {
return this.parameters[index];
}
/**
* <p>Return an immutable {@link Set} of {@link String}s containing all
* known {@link Parameters.Parameter Parameter}
* {@link Parameters.Parameter#getName() names}.</p>
*/
public Set getNames() {
return this.names;
}
/**
* <p>Return the first {@link String} value associated with the
* specified parameter name, or <b>null</b>.</p>
*/
public String getValue(String name) {
final List values = (List) this.map.get(name);
return values == null ? null : (String) values.get(0);
}
/**
* <p>Return an immutable {@link List} of all {@link String} values
* associated with the specified parameter name, or <b>null</b>.</p>
*/
public List getValues(String name) {
return (List) this.map.get(name);
}
/* ====================================================================== */
/* OBJECT METHODS */
/* ====================================================================== */
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Parameters Parameters} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Parameters Parameters} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
StringBuffer buffer = new StringBuffer();
for (int x = 0; x < this.parameters.length; x ++) {
buffer.append(this.delimiter);
buffer.append(this.parameters[x].toString(encoding));
}
return buffer.substring(1);
}
/**
* <p>Return the hash code value of this
* {@link Parameters Parameters} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Parameters Parameters} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, it is a {@link Parameters Parameters}
* instance, and its {@link #toString() string representation} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Parameters)) {
return this.string.equals(((Parameters) object).string);
} else {
return false;
}
}
/* ====================================================================== */
/* PUBLIC INNER CLASSES */
/* ====================================================================== */
/**
* <p>The {@link Parameters.Parameter Parameter} class represents a single
* parameter either parsed from a query string or a path element.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public static class Parameter implements Encodable {
/** <p>The name of the parameter (decoded).</p> */
private final String name;
/** <p>The value of the parameter (decoded).</p> */
private final String value;
/** <p>The encoded {@link String} representation of this.</p> */
private final String string;
/**
* <p>Create a new {@link Parameters.Parameter Parameter} given an
* encoded parameter name and value.</p>
*
* @throws NullPointerException if the name was <b>null</b>.
* @throws IllegalArgumentException if the name was an empty string.
*/
public Parameter(String name, String value) {
if (name == null) throw new NullPointerException();
if (name.length() == 0) throw new IllegalArgumentException();
this.name = name;
this.value = value;
this.string = EncodingTools.toString(this);
}
/* ================================================================== */
/* STATIC CONSTRUCTION METHODS */
/* ================================================================== */
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters.Parameter} instance.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters.Parameter}
* instance or <b>null</b> if the specified string was
* <b>null</b> or empty.
*/
public static Parameter parse(String parameter)
throws UnsupportedEncodingException {
try {
return parse(parameter, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified parameters {@link String} into a
* {@link Parameters.Parameter} instance.</p>
*
* @return a <b>non-null</b> and not empty {@link Parameters.Parameter}
* instance or <b>null</b> if the specified string was
* <b>null</b> or empty.
*/
public static Parameter parse(String parameter, String encoding)
throws UnsupportedEncodingException {
if (parameter == null) return null;
if (encoding == null) encoding = DEFAULT_ENCODING;
String split[] = StringTools.splitOnce(parameter, '=', false);
if (split[0] == null) return null;
return new Parameter(split[0], split[1]);
}
/* ================================================================== */
/* PUBLIC EXPOSED METHODS */
/* ================================================================== */
/**
* <p>Return the URL-decoded name of this
* {@link Parameters.Parameter Parameter} instance.</p>
*/
public String getName() {
return this.name;
}
/**
* <p>Return the URL-decoded value of this
* {@link Parameters.Parameter Parameter} instance.</p>
*/
public String getValue() {
return this.value;
}
/* ================================================================== */
/* OBJECT METHODS */
/* ================================================================== */
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Parameters.Parameter Parameter} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Parameters.Parameter Parameter} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
if (this.value != null) {
return EncodingTools.urlEncode(this.name, encoding) + "=" +
EncodingTools.urlEncode(this.value, encoding);
} else {
return EncodingTools.urlEncode(this.name, encoding);
}
}
/**
* <p>Return the hash code value for this
* {@link Parameters.Parameter Parameter} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Parameters.Parameter Parameter} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, it is a {@link Parameters.Parameter Parameter}
* instance, and its {@link #toString() string representation} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Parameter)) {
return this.string.equals(((Parameter) object).string);
} else {
return false;
}
}
}
}

View File

@ -1,559 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.util.location;
import it.could.util.StringTools;
import it.could.util.encoding.Encodable;
import it.could.util.encoding.EncodingTools;
import java.io.UnsupportedEncodingException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* <p>The {@link Path Path} class is an ordered collection of
* {@link Path.Element Element} instances representing a path
* structure.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class Path extends AbstractList implements Encodable {
/** <p>The array of {@link Path.Element Element}s.</p> */
private final Element paths[];
/** <p>The current {@link Parameters} instance or <b>null</b>.</p> */
private final Parameters parameters;
/** <p>A flag indicating whether this path is absolute or not.</p> */
private final boolean absolute;
/** <p>A flag indicating if this path is a collection or not.</p> */
private final boolean collection;
/** <p>The {@link String} representation of this (encoded).</p> */
private final String string;
/**
* <p>Create a new {@link Path Path} instance.</p>
*
* @throws ClassCastException if any of the elements in the {@link List}
* was not a {@link Path.Element Element}.
*/
public Path(List elements, boolean absolute, boolean collection) {
this(elements, absolute, collection, null);
}
/**
* <p>Create a new {@link Path Path} instance.</p>
*
* @throws ClassCastException if any of the elements in the {@link List}
* was not a {@link Path.Element Element}.
*/
public Path(List elements, boolean absolute, boolean collection,
Parameters parameters) {
final Stack resolved = resolve(null, absolute, elements);
final Element array[] = new Element[resolved.size()];
this.paths = (Element []) resolved.toArray(array);
this.parameters = parameters;
this.absolute = absolute;
this.collection = collection;
this.string = EncodingTools.toString(this);
}
/* ====================================================================== */
/* STATIC CONSTRUCTION METHODS */
/* ====================================================================== */
/**
* <p>Parse the specified {@link String} into a {@link Path} structure.</p>
*/
public static Path parse(String path) {
try {
return parse(path, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified {@link String} into a {@link Path} structure.</p>
*/
public static Path parse(String path, String encoding)
throws UnsupportedEncodingException {
final List params = new ArrayList();
final List elems = new ArrayList();
/* No path, flog it! */
if ((path == null) || (path.length() == 0)) {
return new Path(elems, false, false, null);
}
/* Check for a proper encoding */
if (encoding == null) encoding = DEFAULT_ENCODING;
/* Split up the path structure into its path element components */
final String split[] = StringTools.splitAll(path, '/');
/* Check if this path is an absolute path */
final boolean absolute = path.charAt(0) == '/';
/* Check every single path element and append it to the current one */
Element element = null;
for (int x = 0; x < split.length; x++) {
if (split[x] == null) continue; /* Collapse double slashes */
element = parsePath(split[x], params, encoding);
if (element != null) elems.add(element);
}
/* Check if this is a collection */
final boolean collection = ((split[split.length - 1] == null)
|| (element == null)
|| element.getName().equals(".")
|| element.getName().equals(".."));
/* Setup the last path in our chain and return the first one */
final Parameters parameters = Parameters.create(params, ';');
return new Path(elems, absolute, collection, parameters);
}
/* ====================================================================== */
/**
* <p>Parse a single path element like <code>path!extra;param</code>.</p>
*/
private static Element parsePath(String path, List parameters,
String encoding)
throws UnsupportedEncodingException {
final int pathEnds = StringTools.findFirst(path, "!;");
final Element element;
if (pathEnds < 0) {
element = new Element(EncodingTools.urlDecode(path, encoding), null);
} else if (path.charAt(pathEnds) == ';') {
// --> pathname;pathparameter
final String name = path.substring(0, pathEnds);
final String param = path.substring(pathEnds + 1);
final Parameters params = Parameters.parse(param, ';', encoding);
if (params != null) parameters.addAll(params);
element = new Element(EncodingTools.urlDecode(name, encoding), null);
} else {
// --> pathname!extra...
final String name = path.substring(0, pathEnds);
final String more = path.substring(pathEnds + 1);
final String split[] = StringTools.splitOnce(more, ';', false);
final Parameters params = Parameters.parse(split[1], ';', encoding);
if (params != null) parameters.addAll(params);
element = new Element(EncodingTools.urlDecode(name, encoding),
EncodingTools.urlDecode(split[0], encoding));
}
if (element.toString().length() == 0) return null;
return element;
}
/* ====================================================================== */
/* RESOLUTION METHODS */
/* ====================================================================== */
/**
* <p>Resolve the specified {@link Path} against this one.</p>
*/
public Path resolve(Path path) {
/* Merge the parameters */
final List params = new ArrayList();
if (this.parameters != null) params.addAll(this.parameters);
if (path.parameters != null) params.addAll(path.parameters);
final Parameters parameters = Parameters.create(params, ';');
/* No path, return this instance */
if (path == null) return this;
/* If the target is absolute, only merge the parameters */
if (path.absolute)
return new Path(path, true, path.collection, parameters);
/* Resolve the path */
final Stack source = new Stack();
source.addAll(this);
if (! this.collection && (source.size() > 0)) source.pop();
final List resolved = resolve(source, this.absolute, path);
/* Figure out if the resolved path is a collection and return it */
final boolean c = path.size() == 0 ? this.collection : path.collection;
return new Path(resolved, this.absolute, c, parameters);
}
/**
* <p>Parse the specified {@link String} into a {@link Path} and resolve it
* against this one.</p>
*/
public Path resolve(String path) {
try {
return this.resolve(parse(path, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified {@link String} into a {@link Path} and resolve it
* against this one.</p>
*
* @throws NullPointerException if the path {@link String} was <b>null</b>.
*/
public Path resolve(String path, String encoding)
throws UnsupportedEncodingException {
if (encoding == null) encoding = DEFAULT_ENCODING;
if (path == null) return this;
return this.resolve(parse(path, encoding));
}
/* ====================================================================== */
private static Stack resolve(Stack stack, boolean absolute, List elements) {
/* If we have no source stack we create a new empty one */
if (stack == null) stack = new Stack();
/* A flag indicating whether we are at the "root" path element. */
boolean atroot = absolute && stack.empty();
/* Iterate through the current path elements to see what to do. */
for (Iterator iter = elements.iterator(); iter.hasNext(); ) {
final Element element = (Element) iter.next();
/* If this is the "." (current) path element, skip it. */
if (".".equals(element.getName())) continue;
/* If this is the ".." (parent) path element, it gets nasty. */
if ("..".equals(element.getName())) {
/* The root path's parent is always itself */
if (atroot) continue;
/* We're not at root and have the stack, relative ".." */
if (stack.size() == 0) {
stack.push(element);
/* We're not at root, but we have stuff in the stack */
} else {
/* Get the last element in the stack */
final Element prev = (Element) stack.peek();
/* If the last element is "..", add another one */
if ("..".equals(prev.getName())) stack.push(element);
/* The last element was not "..", pop it out */
else stack.pop();
/* If absoulte and stack is empty, we're at root */
if (absolute) atroot = stack.size() == 0;
}
} else {
/* Normal element processing follows... */
stack.push(element);
atroot = false;
}
}
return stack;
}
/* ====================================================================== */
/* RELATIVIZATION METHODS */
/* ====================================================================== */
/**
* <p>Parse the specified {@link String} into a {@link Path} and relativize
* it against this one.</p>
*/
public Path relativize(String path) {
try {
return this.relativize(parse(path, DEFAULT_ENCODING));
} catch (UnsupportedEncodingException exception) {
final String message = "Unsupported encoding " + DEFAULT_ENCODING;
final InternalError error = new InternalError(message);
throw (InternalError) error.initCause(exception);
}
}
/**
* <p>Parse the specified {@link String} into a {@link Path} and relativize
* it against this one.</p>
*/
public Path relativize(String path, String encoding)
throws UnsupportedEncodingException {
if (encoding == null) encoding = DEFAULT_ENCODING;
return this.relativize(parse(path, encoding));
}
/**
* <p>Retrieve the relativization path from this {@link Path} to the
* specified {@link Path}.</p>
*/
public Path relativize(Path path) {
/* No matter what, always return the aggregate of all parameters */
final List parameters = new ArrayList();
if (this.parameters != null) parameters.addAll(this.parameters);
if (path.parameters != null) parameters.addAll(path.parameters);
final Parameters params = Parameters.create(parameters, ';');
/* We are absolute and the specified path is absolute, we process */
if ((path.absolute) && (this.absolute)) {
/* Find the max number of paths we should examine */
final int num = this.collection ? this.size() : this.size() - 1;
/* Process the two absolute paths to check common elements */
int skip = 0;
for (int x = 0; (x < num) && (x < path.size()); x ++) {
if (path.paths[x].equals(this.paths[x])) skip ++;
else break;
}
/* Figure out if the resulting path is a collection */
final boolean collection;
if (path.size() > skip) collection = path.collection;
else if (this.size() > skip) collection = true;
else collection = this.collection;
/* Recreate the path to return by adding ".." and the paths */
final List elems = new ArrayList();
for (int x = skip; x < num; x ++) elems.add(new Element("..", null));
elems.addAll(path.subList(skip, path.size()));
return new Path(elems, false, collection);
}
/*
* Here we are in one of the following cases:
* - the specified path is already relative, so why bother?
* - we are relative and the specified path is absolute: in this case
* we can't possibly know how far away we are located from the root
* so, we only have one option, to return the absolute path.
* In all cases, though, before returning the specified path, we just
* merge ours and the path's parameters.
*/
if (this.absolute && (! path.absolute)) {
/*
* Ok, let's bother, we're absolute and the specified is not. This
* means that if we resolve the path, we can find another absolute
* path, and therefore we can do a better job at relativizin it.
*/
return this.relativize(this.resolve(path));
}
/* We'll never going to be able to do better than this */
return new Path(path, path.absolute, path.collection, params);
}
/* ====================================================================== */
/* PUBLIC EXPOSED METHODS */
/* ====================================================================== */
/**
* <p>Return the {@link Path.Element Element} instance at
* the specified index.</p>
*/
public Object get(int index) {
return this.paths[index];
}
/**
* <p>Return the number of {@link Path.Element Element}
* instances contained by this instance.</p>
*/
public int size() {
return this.paths.length;
}
/**
* <p>Checks if this {@link Path Path} instance represents
* an absolute path.</p>
*/
public boolean isAbsolute() {
return this.absolute;
}
/**
* <p>Checks if this {@link Path Path} instance represents
* a collection.</p>
*/
public boolean isCollection() {
return this.collection;
}
/**
* <p>Returns the collection of {@link Parameters Parameters}
* contained by this instance or <b>null</b>.</p>
*/
public Parameters getParameters() {
return this.parameters;
}
/* ====================================================================== */
/* OBJECT METHODS */
/* ====================================================================== */
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Path Path} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Path Path} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
StringBuffer buffer = new StringBuffer();
if (this.absolute) buffer.append('/');
final int last = this.paths.length - 1;
for (int x = 0; x < last; x ++) {
buffer.append(this.paths[x].toString(encoding)).append('/');
}
if (last >= 0) {
buffer.append(this.paths[last].toString(encoding));
if (this.collection) buffer.append('/');
}
if (this.parameters != null)
buffer.append(';').append(this.parameters.toString(encoding));
return buffer.toString();
}
/**
* <p>Return the hash code value of this
* {@link Path Path} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Path Path} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, is a {@link Path Path}
* instance and its {@link #toString() string representation} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Path)) {
return this.string.equals(((Path) object).string);
}
return false;
}
/* ====================================================================== */
/* PUBLIC INNER CLASSES */
/* ====================================================================== */
/**
* <p>The {@link Path.Element Element} class represents a path
* element within the {@link Path Path} structure.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public static class Element implements Encodable {
/** <p>The name of this path element (decoded).</p> */
private final String name;
/** <p>The extra path information of this path element (decoded).</p> */
private final String extra;
/** <p>The {@link String} representation of this (encoded).</p> */
private final String string;
/**
* <p>Create a new {@link Path.Element Element} instance given its
* url-decoded components name and extra.</p>
*
* @throws NullPointerException if the specified name was <b>null</b>.
*/
public Element(String name, String extra) {
if (name == null) throw new NullPointerException("Null path name");
this.name = name;
this.extra = extra;
this.string = EncodingTools.toString(this);
}
/* ================================================================== */
/* PUBLIC EXPOSED METHODS */
/* ================================================================== */
/**
* <p>Return the url-decoded {@link String} name of this
* {@link Path.Element Element}.</p>
*/
public String getName() {
return this.name;
}
/**
* <p>Return the url-decoded {@link String} extra path of this
* {@link Path.Element Element}.</p>
*/
public String getExtra() {
return this.extra;
}
/* ================================================================== */
/* OBJECT METHODS */
/* ================================================================== */
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Path.Element Element} instance.</p>
*/
public String toString() {
return this.string;
}
/**
* <p>Return the URL-encoded {@link String} representation of this
* {@link Path.Element Element} instance using the specified
* character encoding.</p>
*/
public String toString(String encoding)
throws UnsupportedEncodingException {
final StringBuffer buffer = new StringBuffer();
buffer.append(EncodingTools.urlEncode(this.name, encoding));
if (this.extra != null) {
buffer.append('!');
buffer.append(EncodingTools.urlEncode(this.extra, encoding));
}
return buffer.toString();
}
/**
* <p>Return the hash code value of this
* {@link Path.Element Element} instance.</p>
*/
public int hashCode() {
return this.string.hashCode();
}
/**
* <p>Check if the specified {@link Object} is equal to this
* {@link Path.Element Element} instance.</p>
*
* <p>The specified {@link Object} is considered equal to this one if
* it is <b>non-null</b>, is a {@link Path.Element Element}
* instance and its {@link #toString() string representation} equals
* this one's.</p>
*/
public boolean equals(Object object) {
if ((object != null) && (object instanceof Element)) {
return this.string.equals(((Element) object).string);
}
return false;
}
}
}

View File

@ -1,29 +0,0 @@
<html>
<head>
<title>Location Utilities</title>
</head>
<body>
<p>
This package contains a number of utility classes to parse and
work with URLs.
</p>
<p>
The {@link java.net.URL} class already provides most of the functionality
covered by this package, but certain limitations in its implementation
(for example, all schemes <i>must</i> be registered with the
{java.net.URLStreamHandler} class before they can be used), prompted
the re-development of a similar API.
</p>
<p>
For further details on what the different classes in this package mean
and how they interact, see the {@link it.could.util.location.Location}
class documentation, but as a reference, this is a picture outlining
the structure:
</p>
<div align="center">
<a href="url.pdf" target="_new" title="PDF Version">
<img src="url.gif" alt="URL components" border="0">
</a>
</div>
</body>
</html>

View File

@ -1,11 +0,0 @@
<html>
<head>
<title>Encoding Utilities</title>
</head>
<body>
<p>
This package contains a number of utility classes which can come handy
from time to time when writing Java code.
</p>
</body>
</html>

View File

@ -1,132 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.IOException;
import java.io.PrintWriter;
/**
* <p>A {@link RuntimeException} representing a
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* response for a specified {@link DAVResource}.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVException extends RuntimeException {
private DAVResource resource = null;
private int status = 0;
/**
* <p>Create a new {@link DAVException} instance.</p>
*/
public DAVException(int status, String message) {
this(status, message, null, null);
}
/**
* <p>Create a new {@link DAVException} instance.</p>
*/
public DAVException(int status, String message, Throwable throwable) {
this(status, message, throwable, null);
}
/**
* <p>Create a new {@link DAVException} instance.</p>
*/
public DAVException(int status, String message, DAVResource resource) {
this(status, message, null, resource);
}
/**
* <p>Create a new {@link DAVException} instance.</p>
*/
public DAVException(int s, String m, Throwable t, DAVResource r) {
super(m, t);
this.resource = r;
this.status = s;
}
/**
* <p>Return the status code associated with this instance.</p>
*/
public int getStatus() {
return this.status;
}
/**
* <p>Return the {@link DAVResource} associated with this instance.</p>
*/
public DAVResource getResource() {
return this.resource;
}
/**
* <p>Write the body of this {@link DAVException} to the specified
* {@link DAVTransaction}'s output.</p>
*/
public void write(DAVTransaction transaction)
throws IOException {
transaction.setContentType("text/html; charset=\"UTF-8\"");
transaction.setStatus(this.getStatus());
/* Prepare and log the error message */
String message = DAVUtilities.getStatusMessage(this.getStatus());
if (message == null) {
transaction.setStatus(500);
message = Integer.toString(this.getStatus()) + " Unknown";
}
/* Write the error message to the client */
PrintWriter out = transaction.write("UTF-8");
out.println("<html>");
out.print("<head><title>Error ");
out.print(message);
out.println("</title></head>");
out.println("<body>");
out.print("<p><b>Error ");
out.print(message);
out.println("</b></p>");
/* Check if we have a resource associated with the extension */
if (this.getResource() != null) {
String r = transaction.lookup(this.getResource()).toASCIIString();
out.print("<p>Resource in error: <a href=\"");
out.print(r);
out.println("\">");
out.print(r);
out.println("</a></p>");
}
/* Process any exception and its cause */
Throwable throwable = this;
out.println("<hr /><p>Exception details:</p>");
while (throwable != null) {
out.print("<pre>");
throwable.printStackTrace(out);
out.println("</pre>");
throwable = throwable.getCause();
if (throwable != null) out.println("<hr /><p>Caused by:</p>");
}
/* Close up the HTML */
out.println("</body>");
out.println("</html>");
out.flush();
}
}

View File

@ -1,165 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* <p>A specialized {@link InputStream} to read from {@link DAVResource}s.</p>
*
* <p>This specialized {@link InputStream} never throws {@link IOException}s,
* but rather relies on the unchecked {@link DAVException} to notify the
* framework of the correct DAV errors.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVInputStream extends InputStream {
/** <p>The {@link InputStream} of the source {@link File}. </p> */
protected InputStream input = null;
/** <p>The {@link DAVResource} associated with this instance. </p> */
private DAVResource resource = null;
/**
* <p>Create a new {@link DAVInputStream} instance.</p>
*/
protected DAVInputStream(DAVResource resource) {
if (resource == null) throw new NullPointerException();
init(resource);
}
protected void init(DAVResource resource)
{
try {
this.input = new FileInputStream(resource.getFile());
} catch (IOException e) {
String message = "Unable to read from resource";
throw new DAVException (403, message, e, resource);
}
}
/**
* <p>Read data from this {@link InputStream}.</p>
*/
public int read() {
if (this.input == null) throw new IllegalStateException("Closed");
try {
return input.read();
} catch (IOException e) {
throw new DAVException(403, "Can't read data", e, this.resource);
}
}
/**
* <p>Read data from this {@link InputStream}.</p>
*/
public int read(byte b[]) {
if (this.input == null) throw new IllegalStateException("Closed");
try {
return input.read(b);
} catch (IOException e) {
throw new DAVException(403, "Can't read data", e, this.resource);
}
}
/**
* <p>Read data from this {@link InputStream}.</p>
*/
public int read(byte b[], int off, int len) {
if (this.input == null) throw new IllegalStateException("Closed");
try {
return input.read(b, off, len);
} catch (IOException e) {
throw new DAVException(403, "Can't read data", e, this.resource);
}
}
/**
* <p>Skip a specified amount of data reading from this
* {@link InputStream}.</p>
*/
public long skip(long n) {
if (this.input == null) throw new IllegalStateException("Closed");
try {
return input.skip(n);
} catch (IOException e) {
throw new DAVException(403, "Can't skip over", e, this.resource);
}
}
/**
* <p>Return the number of bytes that can be read or skipped from this
* {@link InputStream} without blocking.</p>
*/
public int available() {
if (this.input == null) throw new IllegalStateException("Closed");
try {
return input.available();
} catch (IOException e) {
throw new DAVException(403, "Can't skip over", e, this.resource);
}
}
/**
* <p>Return the number of bytes that can be read or skipped from this
* {@link InputStream} without blocking.</p>
*/
public void close() {
if (this.input == null) return;
try {
this.input.close();
} catch (IOException e) {
throw new DAVException(403, "Can't close", e, this.resource);
} finally {
this.input = null;
}
}
/**
* <p>Marks the current position in this {@link InputStream}.</p>
*/
public void mark(int readlimit) {
if (this.input == null) throw new IllegalStateException("Closed");
this.input.mark(readlimit);
}
/**
* <p>Repositions this stream to the position at the time the
* {@link #mark(int)} method was last called on this
* {@link InputStream}.</p>
*/
public void reset() {
if (this.input == null) throw new IllegalStateException("Closed");
try {
input.reset();
} catch (IOException e) {
throw new DAVException(403, "Can't reset", e, this.resource);
}
}
/**
* <p>Tests if this {@link InputStream} supports the {@link #mark(int)}
* and {@link #reset()} methods.</p>
*/
public boolean markSupported() {
if (this.input == null) throw new IllegalStateException("Closed");
return this.input.markSupported();
}
}

View File

@ -1,46 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
/**
* <p>A simple interface identifying a {@link DAVRepository} event listener.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public interface DAVListener {
/** <p>An event representing the creation of a collection.</p> */
public static final int COLLECTION_CREATED = 1;
/** <p>An event representing the deletion of a collection.</p> */
public static final int COLLECTION_REMOVED = 2;
/** <p>An event representing the creation of a resource.</p> */
public static final int RESOURCE_CREATED = 3;
/** <p>An event representing the deletion of a resource.</p> */
public static final int RESOURCE_REMOVED = 4;
/** <p>An event representing the modification of a resource.</p> */
public static final int RESOURCE_MODIFIED = 5;
/**
* <p>Notify this {@link DAVListener} of an action occurred on a
* specified {@link DAVResource}.</p>
*
* @param resource the {@link DAVResource} associated with the notification.
* @param event a number identifying the type of the notification.
*/
public void notify(DAVResource resource, int event);
}

View File

@ -1,86 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
/**
* <p>A simplicisting class defining an esay way to log stuff to the
* {@link ServletContext}.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVLogger {
private final ServletContext context;
private final String servletName;
private final boolean debug;
/**
* <p>Create a new {@link DAVLogger} from a {@link ServletConfig}.</p>
*/
public DAVLogger(ServletConfig config, boolean debug) {
this.context = config.getServletContext();
this.servletName = config.getServletName();
this.debug = debug;
}
/**
* <p>Log a debug message to the context logger.</p>
*/
public void debug(String message) {
if (this.debug) this.doLog(message, null);
}
/**
* <p>Log a debug message and related exception to the context logger.</p>
*/
public void debug(String message, Throwable throwable) {
if (this.debug) this.doLog(message, throwable);
}
/**
* <p>Log a message to the context logger.</p>
*/
public void log(String message) {
this.doLog(message, null);
}
/**
* <p>Log a message and related exception to the context logger.</p>
*/
public void log(String message, Throwable throwable) {
this.doLog(message, throwable);
}
/**
* <p>Internal method for formatting messages and logging.</p>
*/
private void doLog(String message, Throwable throwable) {
if ((message == null) && (throwable == null)) return;
if ((message == null) || ("".equals(message))) message = "No message";
StringBuffer buffer = new StringBuffer();
buffer.append('[');
buffer.append(this.servletName);
buffer.append("] ");
buffer.append(message);
if (throwable == null) this.context.log(buffer.toString());
else this.context.log(buffer.toString(), throwable);
}
}

View File

@ -1,41 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.IOException;
/**
* <p>An interface describing the implementation of a
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* method.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public interface DAVMethod {
/**
* <p>Process the specified {@link DAVTransaction}.</p>
*
* @param transaction An object encapsulaing a WebDAV request/response.
* @param resource The {@link DAVResource} to process.
* @throws IOException If an I/O error occurred.
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException;
}

View File

@ -1,149 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* <p>A {@link DAVException} representing a
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>207</code> (Multi-Status) response.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVMultiStatus extends DAVException {
private Set responses = new HashSet();
/**
* <p>Create a new {@link DAVMultiStatus} instance.</p>
*/
public DAVMultiStatus() {
super(207, "Multi-Status response");
}
/**
* <p>Write the body of the multi-status response to the specified
* {@link DAVTransaction}'s output.</p>
*/
public void write(DAVTransaction transaction)
throws IOException {
/* What to do on a collection resource */
transaction.setStatus(207);
transaction.setContentType("text/xml; charset=\"UTF-8\"");
PrintWriter out = transaction.write("UTF-8");
/* Output the XML declaration and the root document tag */
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("<D:multistatus xmlns:D=\"DAV:\">");
Iterator responses = this.responses.iterator();
while (responses.hasNext()) {
Response response = (Response) responses.next();
out.println(" <D:response>");
out.print(" <D:href>");
out.print(transaction.lookup(response.resource));
out.println("</D:href>");
if (response.status != 0) {
out.print(" <D:status>HTTP/1.1 ");
out.print(DAVUtilities.getStatusMessage(response.status));
out.println("</D:status>");
}
if (response.message != null) {
out.print(" <D:responsedescription>");
out.print(response.message);
out.println("</D:responsedescription>");
}
out.println(" </D:response>");
}
out.println("</D:multistatus>");
out.flush();
}
/**
* <p>Return the number of responses held in this instance.</p>
*/
public int size() {
return this.responses.size();
}
/**
* <p>Merge the responses held into the specified {@link DAVMultiStatus}
* into this instance.</p>
*/
public void merge(DAVMultiStatus multistatus) {
if (multistatus == null) return;
Iterator iterator = multistatus.responses.iterator();
while (iterator.hasNext()) this.responses.add(iterator.next());
}
/**
* <p>Merge the details held into the specified {@link DAVException}
* into this instance.</p>
*/
public void merge(DAVException exception) {
DAVResource resource = exception.getResource();
if (resource == null) throw exception;
int status = exception.getStatus();
String message = exception.getMessage();
this.responses.add(new Response(resource, status, message));
}
private static class Response implements Comparable {
private DAVResource resource = null;
private int status = 0;
private String message = null;
public Response(Response response) {
this(response.resource, response.status, response.message);
}
public Response(DAVResource resource, int status, String message) {
if (resource == null) throw new NullPointerException();
this.resource = resource;
this.status = status;
this.message = message;
}
public int hashCode() {
return this.resource.hashCode();
}
public int compareTo(Object object) {
Response response = (Response) object;
return (this.resource.compareTo(response.resource));
}
public boolean equals(Object object) {
if (object instanceof Response) {
Response response = (Response) object;
return (this.resource.equals(response.resource));
}
return false;
}
}
}

View File

@ -1,56 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.IOException;
/**
* <p>A simple {@link DAVException} encapsulating an
* <a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a> not modified
* response.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVNotModified extends DAVException {
private DAVResource resource = null;
/**
* <p>Create a new {@link DAVNotModified} instance.</p>
*/
public DAVNotModified(DAVResource resource) {
super(304, "Resource Not Modified");
this.resource = resource;
}
/**
* <p>Write the body of this {@link DAVNotModified} to the specified
* {@link DAVTransaction}'s output.</p>
*/
public void write(DAVTransaction transaction)
throws IOException {
transaction.setStatus(this.getStatus());
/* Figure out what we're dealing with here */
String etag = resource.getEntityTag();
String lmod = DAVUtilities.formatHttpDate(resource.getLastModified());
/* Set the normal headers that are required for a GET */
if (etag != null) transaction.setHeader("ETag", etag);
if (lmod != null) transaction.setHeader("Last-Modified", lmod);
}
}

View File

@ -1,187 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* <p>A specialized {@link OutputStream} to write to {@link DAVResource}s.</p>
*
* <p>When writing to this {@link OutputStream} the data will be written to
* a temporary file. This temporary file will be moved to its final destination
* (the original file identifying the resource) when the {@link #close()}
* method is called.</p>
*
* <p>This specialized {@link OutputStream} never throws {@link IOException}s,
* but rather relies on the unchecked {@link DAVException} to notify the
* framework of the correct DAV errors.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVOutputStream extends OutputStream {
/** <p>The original resource {@link File}.</p> */
private File temporary = null;
/** <p>The {@link OutputStream} of the temporary {@link File}. </p> */
protected OutputStream output = null;
/** <p>The {@link DAVResource} associated with this instance. </p> */
private DAVResource resource = null;
/**
* <p>Create a new {@link DAVOutputStream} instance.</p>
*/
protected DAVOutputStream(DAVResource resource) {
if (resource == null) throw new NullPointerException();
this.resource = resource;
init(resource);
}
protected void init(DAVResource resource) {
try {
this.temporary = resource.getParent().getFile();
this.temporary = File.createTempFile(DAVResource.PREFIX,
DAVResource.SUFFIX,
this.temporary);
this.output = new FileOutputStream(this.temporary);
} catch (IOException e) {
String message = "Unable to create temporary file";
throw new DAVException(507, message, e, resource);
}
}
/**
* <p>Rename the temporary {@link File} to the original one.</p>
*/
protected void rename(File temporary, File original)
throws IOException {
if ((original.exists()) && (!original.delete())) {
throw new IOException("Unable to delete original file");
}
if (!temporary.renameTo(original)) {
throw new IOException("Unable to rename temporary file");
}
}
/**
* <p>Abort any data written to the temporary file and delete it.</p>
*/
public void abort() {
if (this.temporary.exists()) this.temporary.delete();
if (this.output != null) try {
this.output.close();
} catch (IOException exception) {
// Swallow the IOException on close
} finally {
this.output = null;
}
}
/**
* <p>Close this {@link OutputStream} {@link #rename(File,File) renaming}
* the temporary file to the {@link DAVResource#getFile() original} one.</p>
*/
public void close() {
if (this.output == null) return;
try {
/* What kind of event should this invocation trigger? */
int event = this.resource.getFile().exists() ?
DAVListener.RESOURCE_MODIFIED:
DAVListener.RESOURCE_CREATED;
/* Make sure that everything is closed and named properly */
this.output.close();
this.output = null;
this.rename(this.temporary, this.resource.getFile());
/* Send notifications to all listeners of the repository */
this.resource.getRepository().notify(this.resource, event);
} catch (IOException e) {
String message = "Error processing temporary file";
throw new DAVException(507, message, e, this.resource);
} finally {
this.abort();
}
}
/**
* <p>Flush any unwritten data to the disk.</p>
*/
public void flush() {
if (this.output == null) throw new IllegalStateException("Closed");
try {
this.output.flush();
} catch (IOException e) {
this.abort();
String message = "Unable to flush buffers";
throw new DAVException(507, message, e, this.resource);
}
}
/**
* <p>Write data to this {@link OutputStream}.</p>
*/
public void write(int b) {
if (this.output == null) throw new IllegalStateException("Closed");
try {
this.output.write(b);
} catch (IOException e) {
this.abort();
String message = "Unable to write data";
throw new DAVException(507, message, e, this.resource);
}
}
/**
* <p>Write data to this {@link OutputStream}.</p>
*/
public void write(byte b[]) {
if (this.output == null) throw new IllegalStateException("Closed");
try {
this.output.write(b);
} catch (IOException e) {
this.abort();
String message = "Unable to write data";
throw new DAVException(507, message, e, this.resource);
}
}
/**
* <p>Write data to this {@link OutputStream}.</p>
*/
public void write(byte b[], int o, int l) {
if (this.output == null) throw new IllegalStateException("Closed");
try {
this.output.write(b, o, l);
} catch (IOException e) {
this.abort();
String message = "Unable to write data";
throw new DAVException(507, message, e, this.resource);
}
}
/**
* <p>Finalize this {@link DAVOutputStream} instance.</p>
*/
public void finalize() {
this.abort();
}
}

View File

@ -1,92 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
/**
* <p>The <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* transactions processor.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVProcessor {
/** <p>All the implemented methods, comma separated.</p> */
public static final String METHODS = "COPY,DELETE,GET,HEAD,MKCOL,MOVE," +
"OPTIONS,PROPFIND,PROPPATCH,PUT";
/** <p>A static map of all known webdav methods.</p> */
private static Map INSTANCES = new HashMap();
static {
/* Load and verify all the known methods */
final String thisName = DAVProcessor.class.getName();
final int packageDelimiter = thisName.lastIndexOf('.');
final String packageName = packageDelimiter < 1 ? "methods." :
thisName.substring(0, packageDelimiter) + ".methods.";
final StringTokenizer tokenizer = new StringTokenizer(METHODS, ",");
final ClassLoader classLoader = DAVProcessor.class.getClassLoader();
while (tokenizer.hasMoreTokens()) try {
final String method = tokenizer.nextToken();
final String className = packageName + method;
final Class clazz = classLoader.loadClass(className);
INSTANCES.put(method, (DAVMethod) clazz.newInstance());
} catch (Throwable throwable) {
InternalError error = new InternalError("Error loading method");
throw (InternalError) error.initCause(throwable);
}
}
/** <p>The {@link DAVRepository} associated with this instance.</p> */
private DAVRepository repository = null;
/**
* <p>Create a new {@link DAVProcessor} instance.</p>
*/
public DAVProcessor(DAVRepository repository) {
if (repository == null) throw new NullPointerException();
this.repository = repository;
}
/**
* <p>Process the specified {@link DAVTransaction} fully.</p>
*/
public void process(DAVTransaction transaction)
throws IOException {
try {
String method = transaction.getMethod();
if (INSTANCES.containsKey(method)) {
String path = transaction.getNormalizedPath();
DAVResource resource = this.repository.getResource(path);
DAVMethod instance = ((DAVMethod) INSTANCES.get(method));
instance.process(transaction, resource);
} else {
String message = "Method \"" + method + "\" not implemented";
throw new DAVException(501, message);
}
} catch (DAVException exception) {
exception.write(transaction);
}
}
public void setMethod( String methodKey, DAVMethod method ) {
INSTANCES.put( methodKey, method );
}
}

View File

@ -1,164 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* <p>A simple class representing a {@link File} based WebDAV repository.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVRepository {
/** <p>A {@link String} of all acceptable characters in a URI.</p> */
private static final String ACCEPTABLE =
"ABCDEFGHIJLKMNOPQRSTUVWXYZ" + // ALPHA (UPPER)
"abcdefghijklmnopqrstuvwxyz" + // ALPHA (LOWER)
"0123456789" + // DIGIT
"_-!.~'()*" + // UNRESERVED
",;:$&+=" + // PUNCT
"?/[]@"; // RESERVED
/** <p>The {@link File} identifying the root of this repository.</p> */
protected File root = null;
/** <p>The {@link URI} associated with the root of this repository.</p> */
protected URI base = null;
/** <p>The {@link Set} of all configured {@link DAVListener}s.</p> */
private Set listeners = new HashSet();
/**
* <p>Create a new {@link DAVRepository} instance.</p>
*
* @param root The {@link File} identifying the root of the repository.
* @throws IOException If the specified root is not a directory.
* @throws NullPointerExceptoin If the specified root was <b>null</b>.
*/
public DAVRepository(File root)
throws IOException {
init(root);
}
protected void init(File root)
throws IOException {
if (root == null) throw new NullPointerException("Null root");
if (root.isDirectory()) {
this.root = root.getCanonicalFile();
this.base = this.root.toURI().normalize();
} else {
throw new IOException("Root \"" + root + "\" is not a directory");
}
}
/**
* <p>Return the {@link URI} representing the root directory of this
* {@link DAVRepository}.</p>
*
* @return a <b>non-null</b> {@link URI} instance.
*/
protected URI getRepositoryURI() {
return (this.base);
}
/**
* <p>Return the {@link DAVResource} associated with the given name.</p>
*
* @param name a {@link String} identifying the resource name.
* @return a <b>non-null</b> {@link DAVResource} instance.
* @throws IOException If the resource could not be resolved.
*/
public DAVResource getResource(String name)
throws IOException {
if (name == null) return this.getResource((URI) null);
try {
/* Encode the string into a URI */
StringBuffer buffer = new StringBuffer();
byte encoded[] = name.getBytes("UTF-8");
for (int x = 0; x < encoded.length; x ++) {
if (ACCEPTABLE.indexOf((int)encoded[x]) < 0) {
buffer.append('%');
buffer.append(DAVUtilities.toHexString(encoded[x]));
continue;
}
buffer.append((char) encoded[x]);
}
return this.getResource(new URI(buffer.toString()));
} catch (URISyntaxException exception) {
String message = "Invalid resource name \"" + name + "\"";
throw (IOException) new IOException(message).initCause(exception);
}
}
/**
* <p>Return the {@link DAVResource} associated with a {@link URI}.</p>
*
* <p>If the specified {@link URI} is relative it will be resolved against
* the root of this {@link DAVRepository}.</p>
*
* @param uri an absolute or relative {@link URI} identifying the resource.
* @return a <b>non-null</b> {@link DAVResource} instance.
* @throws IOException If the resource could not be resolved.
*/
public DAVResource getResource(URI uri)
throws IOException {
if (uri == null) return new DAVResource(this, this.root);
if (! uri.isAbsolute()) uri = this.base.resolve(uri).normalize();
return new DAVResource(this, new File(uri).getAbsoluteFile());
}
/**
* <p>Add a new {@link DAVListener} to the list of instances notified by
* this {@link DAVRepository}.</p>
*/
public void addListener(DAVListener listener) {
if (listener != null) this.listeners.add(listener);
}
/**
* <p>Remove a {@link DAVListener} from the list of instances notified by
* this {@link DAVRepository}.</p>
*/
public void removeListener(DAVListener listener) {
if (listener != null) this.listeners.remove(listener);
}
/**
* <p>Notify all configured {@link DAVListener}s of an event.</p>
*/
protected void notify(DAVResource resource, int event) {
if (resource == null) throw new NullPointerException("Null resource");
if (resource.getRepository() != this)
throw new IllegalArgumentException("Invalid resource");
Iterator iterator = this.listeners.iterator();
while (iterator.hasNext()) try {
((DAVListener)iterator.next()).notify(resource, event);
} catch (RuntimeException exception) {
// Swallow any RuntimeException thrown by listeners.
}
}
}

View File

@ -1,514 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* <p>A simple representation of a WebDAV resource based on {@link File}s.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVResource implements Comparable {
/** <p>The mime type when {@link #isCollection()} is <b>true</b>.</p> */
public static final String COLLECTION_MIME_TYPE = "httpd/unix-directory";
/** <p>The prefix for all temporary resources.</p> */
protected static final String PREFIX = ".dav_";
/** <p>The suffix for all temporary resources.</p> */
protected static final String SUFFIX = ".temp";
/** <p>The {@link DAVRepository} instance containing this resource.</p> */
private DAVRepository repository = null;
/** <p>The {@link File} associated with this resource.</p> */
private File file = null;
/* ====================================================================== */
/* Constructors */
/* ====================================================================== */
/**
* <p>Create a new {@link DAVResource} instance.</p>
*/
protected DAVResource(DAVRepository repo, File file) {
if (repo == null) throw new NullPointerException("Null repository");
if (file == null) throw new NullPointerException("Null resource");
init(repo, file);
}
protected void init(DAVRepository repo, File file)
{
this.repository = repo;
this.file = file;
if (this.getRelativeURI().isAbsolute())
throw new DAVException(412, "Error relativizing resource");
}
/* ====================================================================== */
/* Generic object methods */
/* ====================================================================== */
/**
* <p>Return an integer number for the hash value of this instance.</p>
*/
public int hashCode() {
return this.file.hashCode();
}
/**
* <p>Compare this instance to another object for equality.</p>
*/
public boolean equals(Object object) {
if (object == null) return (false);
if (object instanceof DAVResource) {
DAVResource resource = (DAVResource) object;
boolean u = this.file.equals(resource.file);
boolean r = this.repository == resource.repository;
return (u && r);
} else {
return (false);
}
}
/**
* <p>Compare this instance to another object for sorting.</p>
*/
public int compareTo(Object object) {
DAVResource resource = (DAVResource) object;
return (this.file.compareTo(resource.file));
}
/* ====================================================================== */
/* Resource checkers */
/* ====================================================================== */
/**
* <p>Checks if this {@link DAVResource} is a null (non existant) one.</p>
*
* @return <b>true</b> if this resource does not esist (is a null resource).
*/
public boolean isNull() {
return (! this.file.exists());
}
/**
* <p>Checks if this {@link DAVResource} is a collection.</p>
*
* @return <b>true</b> if this resource is a collection.
*/
public boolean isCollection() {
if (this.isNull()) return false;
return (this.file.isDirectory());
}
/**
* <p>Checks if this {@link DAVResource} is an existing resource.</p>
*
* @return <b>true</b> if this resource is a collection.
*/
public boolean isResource() {
if (this.isNull()) return false;
return (! this.isCollection());
}
/* ====================================================================== */
/* Resource methods */
/* ====================================================================== */
/**
* <p>Return the {@link File} associated with this resource.</p>
*/
protected File getFile() {
return this.file;
}
/**
* <p>Return the {@link DAVRepository} associated with this resource.</p>
*/
public DAVRepository getRepository() {
return this.repository;
}
/**
* <p>Return the bare name of this resource (without any &quot;/&quot;
* slashes at the end if it is a collection).</p>
*
* @return a <b>non null</b> {@link String}.
*/
public String getName() {
return this.file.getName();
}
/**
* <p>Return the display name of this resource (with an added &quot;/&quot;
* slash at the end if it is a collection).</p>
*
* @return a <b>non null</b> {@link String}.
*/
public String getDisplayName() {
String name = this.getName();
if (this.isCollection()) return (name + "/");
return name;
}
/**
* <p>Return the path of this {@link DAVResource} relative to the root
* of the associated {@link DAVRepository}.</p>
*
* @return a <b>non null</b> {@link String}.
*/
public String getRelativePath() {
return this.getRelativeURI().toASCIIString();
}
/**
* <p>Return the {@link URI} of this {@link DAVResource} relative to the
* root of the associated {@link DAVRepository}.</p>
*
* @return a <b>non-null</b> {@link URI} instance.
*/
public URI getRelativeURI() {
URI uri = this.file.toURI();
return this.repository.getRepositoryURI().relativize(uri).normalize();
}
/**
* <p>Return the parent {@link DAVResource} of this instance.</p>
*
* @return a <b>non-null</b> {@link DAVResource} instance or <b>null</b>
* if this {@link DAVResource} is the repository root.
*/
public DAVResource getParent() {
try {
return new DAVResource(this.repository, this.file.getParentFile());
} catch (Throwable throwable) {
return null;
}
}
/**
* <p>Return an {@link Iterator} over all children of this instance.</p>
*
* @return a <b>non-null</b> {@link Iterator} instance or <b>null</b> if
* this {@link DAVResource} is not a collection.
* @throws IOException If the resource could not be resolved.
*/
public Iterator getChildren() {
if (! this.isCollection()) return null;
File children[] = this.file.listFiles();
if (children == null) children = new File[0];
List resources = new ArrayList(children.length);
for (int x = 0; x < children.length; x++) {
String c = children[x].getName();
if (c.startsWith(PREFIX) && c.endsWith(SUFFIX)) continue;
resources.add(new DAVResource(this.repository, children[x]));
}
return resources.iterator();
}
/* ====================================================================== */
/* DAV Properties */
/* ====================================================================== */
/**
* <p>Return the MIME Content-Type of this {@link DAVResource}.</p>
*
* <p>If the {@link #isCollection()} method returns <b>true</b> this
* method always returns <code>text/html</code>.</p>
*
* @return a {@link String} instance or <b>null</b> if this resource does
* not exist.
*/
public String getContentType() {
if (this.isNull()) return null;
if (this.isCollection()) return COLLECTION_MIME_TYPE;
return DAVUtilities.getMimeType(this.getDisplayName());
}
/**
* <p>Return the MIME Content-Length of this {@link DAVResource}.</p>
*
* @return a {@link Long} instance or <b>null</b> if this resource does
* not exist or is a collection.
*/
public Long getContentLength() {
if (this.isNull() || this.isCollection()) return null;
return new Long(this.file.length());
}
/**
* <p>Return the creation date of this {@link DAVResource}.</p>
*
* <p>As this implementation relies on a {@link File} backend, this method
* will always return the same as {@link #getLastModified()}.</p>
*
* @return a {@link String} instance or <b>null</b> if this resource does
* not exist.
*/
public Date getCreationDate() {
if (this.isNull()) return null;
return new Date(this.file.lastModified());
}
/**
* <p>Return the last modification date of this {@link DAVResource}.</p>
*
* @return a {@link String} instance or <b>null</b> if this resource does
* not exist.
*/
public Date getLastModified() {
if (this.isNull()) return null;
return new Date(this.file.lastModified());
}
/**
* <p>Return a {@link String} representing the Entity Tag of this
* {@link DAVResource} as described by the
* <a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP RFC</a>.</p>
*
* @return a {@link String} instance or <b>null</b> if this resource does
* not exist.
*/
public String getEntityTag() {
if (this.isNull()) return null;
String path = this.getRelativePath();
StringBuffer etag = new StringBuffer();
etag.append('"');
/* Append the MD5 hash of this resource name */
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
digester.reset();
digester.update(path.getBytes("UTF8"));
etag.append(DAVUtilities.toHexString(digester.digest()));
etag.append('-');
} catch (Exception e) {
// If we can't get the MD5 HASH, let's ignore and hope...
}
/* Append the hashCode of this resource name */
etag.append(DAVUtilities.toHexString(path.hashCode()));
/* Append the last modification date if possible */
Date date = this.getLastModified();
if (date != null) {
etag.append('-');
etag.append(DAVUtilities.toHexString(date.getTime()));
}
/* Close the ETag */
etag.append('"');
return(etag.toString());
}
/* ====================================================================== */
/* DAV Operations */
/* ====================================================================== */
/**
* <p>Delete this resource.</p>
*
* @throws DAVException If for any reason this resource cannot be deleted.
*/
public void delete()
throws DAVMultiStatus {
if (this.isNull()) throw new DAVException(404, "Not found", this);
if (this.isResource()) {
if (!windowsSafeDelete(this.file)) {
throw new DAVException(403, "Can't delete resource", this);
} else {
this.repository.notify(this, DAVListener.RESOURCE_REMOVED);
}
} else if (this.isCollection()) {
DAVMultiStatus multistatus = new DAVMultiStatus();
Iterator children = this.getChildren();
while (children.hasNext()) try {
((DAVResource)children.next()).delete();
} catch (DAVException exception) {
multistatus.merge(exception);
}
if (multistatus.size() > 0) throw multistatus;
if (!this.file.delete()) {
throw new DAVException(403, "Can't delete collection", this);
} else {
this.repository.notify(this, DAVListener.COLLECTION_REMOVED);
}
}
}
/**
* <p>Copy this resource to the specified destination.</p>
*
* @throws DAVException If for any reason this resource cannot be deleted.
*/
public void copy(DAVResource dest, boolean overwrite, boolean recursive)
throws DAVMultiStatus {
/*
* NOTE: Since the COPY operation relies on other operation defined in
* this class (and in DAVOutputStream for resources) rather than on
* files temselves, notifications are sent elsewhere, not here.
*/
if (this.isNull()) throw new DAVException(404, "Not found", this);
/* Check if the destination exists and delete if possible */
if (!dest.isNull()) {
if (! overwrite) {
String msg = "Not overwriting existing destination";
throw new DAVException(412, msg, dest);
}
dest.delete();
}
/* Copy a single resource (destination is null as we deleted it) */
if (this.isResource()) {
DAVInputStream in = this.read();
DAVOutputStream out = dest.write();
byte buffer[] = new byte[4096];
int k = -1;
while ((k = in.read(buffer)) != -1) out.write(buffer, 0, k);
in.close();
out.close();
}
/* Copy the collection and all nested members */
if (this.isCollection()) {
dest.makeCollection();
if (! recursive) return;
DAVMultiStatus multistatus = new DAVMultiStatus();
Iterator children = this.getChildren();
while (children.hasNext()) try {
DAVResource childResource = (DAVResource) children.next();
File child = new File(dest.file, childResource.file.getName());
DAVResource target = new DAVResource(this.repository, child);
childResource.copy(target, overwrite, recursive);
} catch (DAVException exception) {
multistatus.merge(exception);
}
if (multistatus.size() > 0) throw multistatus;
}
}
/**
* <p>Moves this resource to the specified destination.</p>
*
* @throws DAVException If for any reason this resource cannot be deleted.
*/
public void move(DAVResource dest, boolean overwrite, boolean recursive)
throws DAVMultiStatus {
// the base class implementation is just copy-then-delete
copy(dest, overwrite, recursive);
this.delete();
}
/**
* <p>Create a collection identified by this {@link DAVResource}.</p>
*
* <p>This resource must be {@link #isNull() non-null} and its
* {@link #getParent() parent} must be accessible and be a
* {@link #isCollection() collection}.</p>
*
* @throws DAVException If for any reason a collection identified by this
* resource cannot be created.
*/
public void makeCollection() {
DAVResource parent = this.getParent();
if (!this.isNull())
throw new DAVException(405, "Resource exists", this);
if (parent.isNull())
throw new DAVException(409, "Parent does not not exist", this);
if (!parent.isCollection())
throw new DAVException(403, "Parent not a collection", this);
if (!this.file.mkdir())
throw new DAVException(507, "Can't create collection", this);
this.repository.notify(this, DAVListener.COLLECTION_CREATED);
}
/**
* <p>Return an {@link InputStream} reading the resource.</p>
*
* @return a <b>non-null</b> {@link InputStream} instance.
*/
public DAVInputStream read() {
if (this.isNull()) throw new DAVException(404, "Not found", this);
if (this.isCollection())
throw new DAVException (403, "Resource is collection", this);
return new DAVInputStream(this);
}
/**
* <p>Return a {@link DAVOutputStream} writing to this {@link DAVResource}
* instance.</p>
*
* @return a <b>non-null</b> {@link DAVOutputStream} instance.
*/
public DAVOutputStream write() {
DAVResource parent = this.getParent();
if (this.isCollection())
throw new DAVException(409, "Can't write a collection", this);
if (parent.isNull())
throw new DAVException(409, "Parent doesn't exist", this);
if (! parent.isCollection())
throw new DAVException(403, "Parent not a collection", this);
return new DAVOutputStream(this);
}
/** File.delete(file) sometimes fails transiently on Windows.
* This occurs even in low-I/O conditions, with file Explorer closed.
* Delete can still fail (correctly) due to the separate Windows problem
* of file sharing violations.
* @return the status of the last attempt of File.delete()
*/
private static boolean windowsSafeDelete(File f)
{
// www.mail-archive.com/java-user@lucene.apache.org/msg08994.html
boolean success = f.delete();
int attempts = 1;
while(!success && f.exists() && attempts < 3) {
if(attempts > 2) {
System.gc();
}
try {
Thread.sleep(20);
} catch (InterruptedException ignore) {
}
success = f.delete();
attempts++;
}
return success;
}
}

View File

@ -1,280 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
/**
* <p>A very simple servlet capable of processing very simple
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* requests.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVServlet implements Servlet, DAVListener {
/** <p>The {@link DAVRepository} configured for this instance.</p> */
protected DAVRepository repository = null;
/** <p>The {@link DAVLogger} configured for this instance.</p> */
protected DAVLogger logger = null;
/** <p>The {@link DAVProcessor} configured for this instance.</p> */
protected DAVProcessor processor = null;
/** <p>The {@link ServletContext} associated with this instance.</p> */
private ServletContext context = null;
/** <p>The {@link ServletConfig} associated with this instance.</p> */
private ServletConfig config= null;
/**
* <p>Create a new {@link DAVServlet} instance.</p>
*/
public DAVServlet() {
super();
}
/**
* <p>Initialize this {@link Servlet} instance.</p>
*
* <p>The only initialization parameter required by this servlet is the
* &quot;<code>rootPath</code>&quot; parameter specifying the path
* of the repository root (either absolute or relative to the configured
* {@link ServletContext}.</p>
*
* <p>If the specified root is relative, it will be considered to
* be relative to the {@link ServletContext} deployment path.</p>
*
* <p>In any case, the specified root must ultimately point to an existing
* directory on a locally-accessible file system.</p>
*
* <p>When set to <code>true</code>, an optional parameter called
* <code>xmlOnly</code> will force this {@link DAVServlet} to use an
* {@link XMLRepository} instead of the default {@link DAVRepository}.</p>
*
* <p>Finally, when set to <code>true</code>, the optional parameter
* <code>debugEnabled</code> will enable logging of method invocation and
* events in the repository.</p>
*/
public void init(ServletConfig config)
throws ServletException {
/* Remember the configuration instance */
this.config = config;
this.context = config.getServletContext();
/* Setup logging */
boolean debug = "true".equals(config.getInitParameter("debugEnabled"));
this.logger = new DAVLogger(config, debug);
/* Try to retrieve the WebDAV root path from the configuration */
String rootPath = config.getInitParameter("rootPath");
if (rootPath == null)
throw new ServletException("Parameter \"rootPath\" not specified");
/* Create repository and processor */
try {
File root = new File(rootPath);
// The repository may not be the local filesystem. It may be rooted at "/".
// But then on Windows new File("/").isAbsolute() is false.
boolean unixAbsolute = rootPath.startsWith("/");
boolean localAbsolute = root.isAbsolute();
if (! unixAbsolute && !localAbsolute) {
URL url = this.context.getResource("/" + rootPath);
if (! "file".equals(url.getProtocol())) {
throw new ServletException("Invalid root \"" + url + "\"");
} else {
root = new File(url.getPath());
}
}
/* Discover the repository implementation at runtime */
String repositoryClass = config.getInitParameter("repositoryClass");
if(repositoryClass != null) {
this.repository = DAVServlet.newRepository(repositoryClass, root);
} else {
// legacy configuration format. keep for now
/* Make sure that we use the correct repository type */
if ("true".equalsIgnoreCase(config.getInitParameter("xmlOnly"))) {
this.repository = new XMLRepository(root);
} else {
this.repository = new DAVRepository(root);
}
}
/* Initialize the processor and register ourselves as listeners */
this.processor = new DAVProcessor(this.repository);
this.repository.addListener(this);
this.logger.log("Initialized from " + root.getPath());
} catch (MalformedURLException e) {
throw new ServletException("Can't resolve \"" + rootPath + "\"", e);
} catch (IOException e) {
String msg = "Can't initialize repository at \"" + rootPath + "\"";
throw new ServletException(msg, e);
}
/* Finally, register this repository in the servlet context */
final String key = getRepositoryKey(config.getServletName());
this.context.setAttribute(key, this.repository);
}
/**
* <p>Retrieve a {@link DAVRepository} for a given {@link File}.</p>
*/
public DAVRepository getRepository(File root)
throws IOException {
return new XMLRepository(root);
}
/**
* <p>Detroy this {@link Servlet} instance.</p>
*/
public void destroy() {
this.repository.removeListener(this);
}
/**
* <p>Return the {@link ServletConfig} associated with this instance.</p>
*/
public ServletConfig getServletConfig() {
return (this.config);
}
/**
* <p>Return the {@link ServletContext} associated with this instance.</p>
*/
public ServletContext getServletContext() {
return (this.context);
}
/**
* <p>Return a informative {@link String} about this servlet.</p>
*/
public String getServletInfo() {
return DAVUtilities.SERVLET_INFORMATION;
}
/**
* <p>Execute the current request.</p>
*/
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
/* Mark our presence */
res.setHeader("Server", this.context.getServerInfo() + ' ' +
DAVUtilities.SERVLET_SIGNATURE);
/* Normal methods are processed by their individual instances */
DAVTransaction transaction = new DAVTransaction(req, res);
try {
this.processor.process(transaction);
} catch (RuntimeException exception) {
final String header = req.getMethod() + ' ' + req.getRequestURI()
+ ' ' + req.getProtocol();
this.context.log("Error processing: " + header);
this.context.log("Exception processing DAV transaction", exception);
throw exception;
}
}
/* ====================================================================== */
/* DAV LISTENER INTERFACE IMPLEMENTATION */
/* ====================================================================== */
/**
* <p>Receive notification of an event occurred in a specific
* {@link DAVRepository}.</p>
*/
public void notify(DAVResource resource, int event) {
String message = "Unknown event";
switch (event) {
case DAVListener.COLLECTION_CREATED:
message = "Collection created";
break;
case DAVListener.COLLECTION_REMOVED:
message = "Collection removed";
break;
case DAVListener.RESOURCE_CREATED:
message = "Resource created";
break;
case DAVListener.RESOURCE_REMOVED:
message = "Resource removed";
break;
case DAVListener.RESOURCE_MODIFIED:
message = "Resource modified";
break;
}
this.logger.debug(message + ": \"" + resource.getRelativePath() + "\"");
}
/* ====================================================================== */
/* CONTEXT METHODS */
/* ====================================================================== */
/**
* <p>Retrieve the key in the {@link ServletContext} where the instance of
* the {@link DAVRepository} associated with a named {@link DAVServlet}
* can be found.</p>
*
* @param servletName the name of the {@link DAVServlet} as specified in
* the <code>web.xml</code> deployment descriptor.</p>
*/
public static String getRepositoryKey(String servletName) {
if (servletName == null) throw new NullPointerException();
return DAVRepository.class.getName() + "." + servletName;
}
/** factory for subclasses configured in web.xml
* @param repositoryClass must extend DAVRepository and have a public constructor(File).
* */
static DAVRepository newRepository(String repositoryClass, File root)
throws ServletException
{
try {
Class c = Class.forName(repositoryClass);
Constructor ctor = c.getConstructor(new Class[]{File.class});
DAVRepository repo = (DAVRepository)ctor.newInstance(new Object[]{root});
return repo;
} catch(ClassNotFoundException e) {
throw new ServletException(e);
} catch(LinkageError le) {
throw new ServletException(le);
} catch(NoSuchMethodException ns) {
throw new ServletException(ns);
} catch(InvocationTargetException it) {
throw new ServletException(it);
} catch(IllegalAccessException ia) {
throw new ServletException(ia);
} catch(InstantiationException ie) {
throw new ServletException(ie);
}
}
}

View File

@ -1,280 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
/**
* <p>A simple wrapper isolating the Java Servlet API from this
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVTransaction {
/**
* <p>The identifyication of the <code>infinity</code> value
* in the <code>Depth</code> header.</p>
*/
public static final int INFINITY = Integer.MAX_VALUE;
/** <p>The nested {@link HttpServletRequest}.</p> */
private HttpServletRequest request = null;
/** <p>The nested {@link HttpServletResponse}.</p> */
private HttpServletResponse response = null;
/** <p>The {@link URI} associated with the base of the repository.</p> */
private URI base = null;
/** <p>The status for the HTTP response.</p> */
private int status = -1;
/* ====================================================================== */
/* Constructors */
/* ====================================================================== */
/**
* <p>Create a new {@link DAVTransaction} instance.</p>
*/
public DAVTransaction(ServletRequest request, ServletResponse response)
throws ServletException {
if (request == null) throw new NullPointerException("Null request");
if (response == null) throw new NullPointerException("Null response");
this.request = (HttpServletRequest) request;
this.response = (HttpServletResponse) response;
this.response.setHeader("DAV", "1");
this.response.setHeader("MS-Author-Via", "DAV");
try {
String scheme = this.request.getScheme();
String host = this.request.getServerName();
String path = this.request.getContextPath() +
this.request.getServletPath();
int port = this.request.getServerPort();
if (! path.endsWith("/")) path += "/";
this.base = new URI(scheme, null, host, port, path, null, null);
this.base = this.base.normalize();
} catch (URISyntaxException exception) {
throw new ServletException("Unable to create base URI", exception);
}
}
/* ====================================================================== */
/* Request methods */
/* ====================================================================== */
/**
* <p>Return the path originally requested by the client.</p>
*/
public String getMethod() {
return this.request.getMethod();
}
/**
* <p>Return the path originally requested by the client.</p>
*/
public String getOriginalPath() {
String path = this.request.getPathInfo();
if (path == null) return "";
if ((path.length() > 1) && (path.charAt(0) == '/')) {
return path.substring(1);
} else {
return path;
}
}
/**
* <p>Return the path originally requested by the client.</p>
*/
public String getNormalizedPath() {
final String path = this.getOriginalPath();
if (! path.endsWith("/")) return path;
return path.substring(0, path.length() - 1);
}
/**
* <p>Return the depth requested by the client for this transaction.</p>
*/
public int getDepth() {
String depth = request.getHeader("Depth");
if (depth == null) return INFINITY;
if ("infinity".equalsIgnoreCase(depth)) return INFINITY;
try {
return Integer.parseInt(depth);
} catch (NumberFormatException exception) {
throw new DAVException(412, "Unable to parse depth", exception);
}
}
/**
* <p>Return a {@link URI}
*/
public URI getDestination() {
String destination = this.request.getHeader("Destination");
if (destination != null) try {
return this.base.relativize(new URI(destination));
} catch (URISyntaxException exception) {
throw new DAVException(412, "Can't parse destination", exception);
}
return null;
}
/**
* <p>Return the overwrite flag requested by the client for this
* transaction.</p>
*/
public boolean getOverwrite() {
String overwrite = request.getHeader("Overwrite");
if (overwrite == null) return true;
if ("T".equals(overwrite)) return true;
if ("F".equals(overwrite)) return false;
throw new DAVException(412, "Unable to parse overwrite flag");
}
/**
* <p>Check if the client requested a date-based conditional operation.</p>
*/
public Date getIfModifiedSince() {
String name = "If-Modified-Since";
if (this.request.getHeader(name) == null) return null;
return new Date(this.request.getDateHeader(name));
}
/* ====================================================================== */
/* Response methods */
/* ====================================================================== */
/**
* <p>Set the HTTP status code of the response.</p>
*/
public void setStatus(int status) {
this.response.setStatus(status);
this.status = status;
}
/**
* <p>Set the HTTP status code of the response.</p>
*/
public int getStatus() {
return this.status;
}
/**
* <p>Set the HTTP <code>Content-Type</code> header.</p>
*/
public void setContentType(String type) {
this.response.setContentType(type);
}
/**
* <p>Set an HTTP header in the response.</p>
*/
public void setHeader(String name, String value) {
this.response.setHeader(name, value);
}
/* ====================================================================== */
/* I/O methods */
/* ====================================================================== */
/**
* <p>Check if there is a body in the request.</p>
*
* <p>This method differs from checking if the return value of the
* {@link #read()} method is not <b>null</b> as a request body of length
* zero will return <b>false</b> in this case, while in the {@link #read()}
* method will return an empty {@link InputStream}.</p>
*/
public boolean hasRequestBody()
throws IOException {
/* We don't support ranges */
if (request.getHeader("Content-Range") != null)
throw new DAVException(501, "Content-Range not supported");
if (this.request.getContentLength() > 0) return true;
String len = this.request.getHeader("Content-Length");
if (len != null) try {
return (Long.parseLong(len) > 0);
} catch (NumberFormatException exception) {
throw new DAVException(411, "Invalid Content-Length specified");
}
return false;
}
/**
* <p>Read from the body of the original request.</p>
*/
public InputStream read()
throws IOException {
/* We don't support ranges */
if (request.getHeader("Content-Range") != null)
throw new DAVException(501, "Content-Range not supported");
if (this.request.getContentLength() >= 0) {
return this.request.getInputStream();
}
String len = this.request.getHeader("Content-Length");
if (len != null) try {
if (Long.parseLong(len) >= 0) return this.request.getInputStream();
} catch (NumberFormatException exception) {
throw new DAVException(411, "Invalid Content-Length specified");
}
return null;
}
/**
* <p>Write the body of the response.</p>
*/
public OutputStream write()
throws IOException {
return this.response.getOutputStream();
}
/**
* <p>Write the body of the response.</p>
*/
public PrintWriter write(String encoding)
throws IOException {
return new PrintWriter(new OutputStreamWriter(this.write(), encoding));
}
/* ====================================================================== */
/* Lookup methods */
/* ====================================================================== */
/**
* <p>Look up the final URI of a {@link DAVResource} as visible from the
* HTTP client requesting this transaction.</p>
*/
public URI lookup(DAVResource resource) {
URI uri = resource.getRelativeURI();
return this.base.resolve(uri).normalize();
}
}

View File

@ -1,420 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TimeZone;
/**
* <p>A collection of static utilities.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public final class DAVUtilities {
/** <p>A {@link HashMap} of configured mime types.</p> */
private static Map MIME_TYPES = new HashMap();
/** <p>A {@link HashMap} of configured mime types.</p> */
private static Properties PROPERTIES = new Properties();
/** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
private static final String FORMAT_822 = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";
/** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
private static final String FORMAT_ISO = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/** <p>The {@link TimeZone} to use for dates.</p> */
private static final TimeZone TIMEZONE = TimeZone.getTimeZone("GMT");
/** <p>The {@link Locale} to use for dates.</p> */
private static final Locale LOCALE = Locale.US;
/**
* <p>Load the mime types map from a resource.</p>
*/
static {
Class clazz = DAVUtilities.class;
ClassLoader loader = clazz.getClassLoader();
/* Load up the properties file */
String webdavPropResource = "plexus-webdav/webdav.props";
InputStream prop = loader.getResourceAsStream(webdavPropResource);
if (prop != null) try {
DAVUtilities.PROPERTIES.load(prop);
prop.close();
} catch (Exception exception) {
exception.printStackTrace();
} else {
System.err.println("Invalid resource: " + webdavPropResource);
}
/* Load up the mime types table */
String mimeTypeResource = "plexus-webdav/mime.types";
InputStream mime = loader.getResourceAsStream(mimeTypeResource);
if (mime != null) try {
InputStreamReader read = new InputStreamReader(mime);
BufferedReader buff = new BufferedReader(read);
String line = null;
while ((line = buff.readLine()) != null) {
line = line.trim();
if (line.length() == 0) continue;
if (line.charAt(0) == '#') continue;
StringTokenizer tokenizer = new StringTokenizer(line);
if (tokenizer.countTokens() > 1) {
String type = tokenizer.nextToken();
while (tokenizer.hasMoreTokens()) {
String extension = '.' + tokenizer.nextToken();
DAVUtilities.MIME_TYPES.put(extension, type);
}
}
}
buff.close();
read.close();
mime.close();
} catch (Exception exception) {
exception.printStackTrace();
} else {
System.err.println("Invalid resource: " + mimeTypeResource);
}
}
/** <p>The signature of this package usable from a servlet.</p> */
public static final String SERVLET_SIGNATURE =
DAVUtilities.getProperty("servlet.signature") + '/' +
DAVUtilities.getProperty("version");
/** <p>The information detail of this package usable from a servlet.</p> */
public static final String SERVLET_INFORMATION =
DAVUtilities.getProperty("servlet.information") + " version " +
DAVUtilities.getProperty("version");
/**
* <p>Deny public construction of {@link DAVUtilities} instances.</p>
*/
private DAVUtilities() {
super();
}
/**
* <p>Return the value of a property configured for this package.</p>
*
* @param name the property name
* @return a {@link String} instance or <b>null</b> if unknown.
*/
public static String getProperty(String name) {
if (name == null) return null;
return DAVUtilities.PROPERTIES.getProperty(name);
}
/**
* <p>Return the MIME Type configured for a given resource.</p>
*
* @param name the resource name whose MIME Type needs to be looked up.
* @return a {@link String} instance or <b>null</b> if the type is unknown.
*/
public static String getMimeType(String name) {
if (name == null) return null;
Iterator iterator = DAVUtilities.MIME_TYPES.keySet().iterator();
while (iterator.hasNext()) {
String extension = (String) iterator.next();
if (name.endsWith(extension)) {
return (String) DAVUtilities.MIME_TYPES.get(extension);
}
}
return null;
}
/**
* <p>Return a {@link String} message given an HTTP status code.</p>
*/
public static String getStatusMessage(int status) {
switch (status) {
/* HTTP/1.1 RFC-2616 */
case 100: return "100 Continue";
case 101: return "101 Switching Protocols";
case 200: return "200 OK";
case 201: return "201 Created";
case 202: return "202 Accepted";
case 203: return "203 Non-Authoritative Information";
case 204: return "204 No Content";
case 205: return "205 Reset Content";
case 206: return "206 Partial Content";
case 300: return "300 Multiple Choices";
case 301: return "301 Moved Permanently";
case 302: return "302 Found";
case 303: return "303 See Other";
case 304: return "304 Not Modified";
case 305: return "305 Use Proxy";
case 306: return "306 (Unused)";
case 307: return "307 Temporary Redirect";
case 400: return "400 Bad Request";
case 401: return "401 Unauthorized";
case 402: return "402 Payment Required";
case 403: return "403 Forbidden";
case 404: return "404 Not Found";
case 405: return "405 Method Not Allowed";
case 406: return "406 Not Acceptable";
case 407: return "407 Proxy Authentication Required";
case 408: return "408 Request Timeout";
case 409: return "409 Conflict";
case 410: return "410 Gone";
case 411: return "411 Length Required";
case 412: return "412 Precondition Failed";
case 413: return "413 Request Entity Too Large";
case 414: return "414 Request-URI Too Long";
case 415: return "415 Unsupported Media Type";
case 416: return "416 Requested Range Not Satisfiable";
case 417: return "417 Expectation Failed";
case 500: return "500 Internal Server Error";
case 501: return "501 Not Implemented";
case 502: return "502 Bad Gateway";
case 503: return "503 Service Unavailable";
case 504: return "504 Gateway Timeout";
case 505: return "505 HTTP Version Not Supported";
/* DAV/1.0 RFC-2518 */
case 102: return "102 Processing";
case 207: return "207 Multi-Status";
case 422: return "422 Unprocessable Entity";
case 423: return "423 Locked";
case 424: return "424 Failed Dependency";
case 507: return "507 Insufficient Storage";
/* Unknown */
default: return null;
}
}
/**
* <p>Format a {@link Number} into a {@link String} making sure that
* {@link NullPointerException}s are not thrown.</p>
*
* @param number the {@link Number} to format.
* @return a {@link String} instance or <b>null</b> if the object was null.
*/
public static String formatNumber(Number number) {
if (number == null) return null;
return (number.toString());
}
/**
* <p>Parse a {@link String} into a {@link Long}.</p>
*
* @param string the {@link String} to parse.
* @return a {@link Long} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Long parseNumber(String string) {
if (string == null) return null;
try {
return new Long(string);
} catch (NumberFormatException exception) {
return null;
}
}
/**
* <p>Format a {@link Date} according to the HTTP/1.1 RFC.</p>
*
* @param date the {@link Date} to format.
* @return a {@link String} instance or <b>null</b> if the date was null.
*/
public static String formatHttpDate(Date date) {
if (date == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
formatter.setTimeZone(TIMEZONE);
return formatter.format(date);
}
/**
* <p>Format a {@link Date} according to the ISO 8601 specification.</p>
*
* @param date the {@link Date} to format.
* @return a {@link String} instance or <b>null</b> if the date was null.
*/
public static String formatIsoDate(Date date) {
if (date == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
formatter.setTimeZone(TIMEZONE);
return formatter.format(date);
}
/**
* <p>Parse a {@link String} into a {@link Date} according to the
* HTTP/1.1 RFC (<code>Mon, 31 Jan 2000 11:59:00 GMT</code>).</p>
*
* @param string the {@link String} to parse.
* @return a {@link Date} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Date parseHttpDate(String string) {
if (string == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
formatter.setTimeZone(TIMEZONE);
try {
return formatter.parse(string);
} catch (ParseException exception) {
return null;
}
}
/**
* <p>Parse a {@link String} into a {@link Date} according to the ISO 8601
* specification (<code>2000-12-31T11:59:00Z</code>).</p>
*
* @param string the {@link String} to parse.
* @return a {@link Date} instance or <b>null</b> if the date was null or
* if there was an error parsing the specified {@link String}.
*/
public static Date parseIsoDate(String string) {
if (string == null) return null;
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
formatter.setTimeZone(TIMEZONE);
try {
return formatter.parse(string);
} catch (ParseException exception) {
return null;
}
}
/**
* <p>Return the HEX representation of an array of bytes.</p>
*
* @param buffer the array of bytes to convert in a HEX {@link String}.
* @return a <b>non-null</b> {@link String} instance.
*/
public static String toHexString(byte buffer[]) {
char output[] = new char[buffer.length * 2];
int position = 0;
for (int x = 0; x < buffer.length; x++) {
output[position ++] = DAVUtilities.toHexDigit(buffer[x] >> 4);
output[position ++] = DAVUtilities.toHexDigit(buffer[x]);
}
return new String(output);
}
/**
* <p>Return the HEX representation of a long integer.</p>
*
* @param number the long to convert in a HEX {@link String}.
* @return a <b>non-null</b> 16-characters {@link String} instance.
*/
public static String toHexString(long number) {
char output[] = new char[16];
output[0] = DAVUtilities.toHexDigit((int)(number >> 60));
output[1] = DAVUtilities.toHexDigit((int)(number >> 56));
output[2] = DAVUtilities.toHexDigit((int)(number >> 52));
output[3] = DAVUtilities.toHexDigit((int)(number >> 48));
output[4] = DAVUtilities.toHexDigit((int)(number >> 44));
output[5] = DAVUtilities.toHexDigit((int)(number >> 40));
output[6] = DAVUtilities.toHexDigit((int)(number >> 36));
output[7] = DAVUtilities.toHexDigit((int)(number >> 32));
output[8] = DAVUtilities.toHexDigit((int)(number >> 28));
output[9] = DAVUtilities.toHexDigit((int)(number >> 24));
output[10] = DAVUtilities.toHexDigit((int)(number >> 20));
output[11] = DAVUtilities.toHexDigit((int)(number >> 16));
output[12] = DAVUtilities.toHexDigit((int)(number >> 12));
output[13] = DAVUtilities.toHexDigit((int)(number >> 8));
output[14] = DAVUtilities.toHexDigit((int)(number >> 4));
output[15] = DAVUtilities.toHexDigit((int)(number));
return new String(output);
}
/**
* <p>Return the HEX representation of an integer.</p>
*
* @param number the int to convert in a HEX {@link String}.
* @return a <b>non-null</b> 8-characters {@link String} instance.
*/
public static String toHexString(int number) {
char output[] = new char[8];
output[0] = DAVUtilities.toHexDigit((int)(number >> 28));
output[1] = DAVUtilities.toHexDigit((int)(number >> 24));
output[2] = DAVUtilities.toHexDigit((int)(number >> 20));
output[3] = DAVUtilities.toHexDigit((int)(number >> 16));
output[4] = DAVUtilities.toHexDigit((int)(number >> 12));
output[5] = DAVUtilities.toHexDigit((int)(number >> 8));
output[6] = DAVUtilities.toHexDigit((int)(number >> 4));
output[7] = DAVUtilities.toHexDigit((int)(number));
return new String(output);
}
/**
* <p>Return the HEX representation of a char.</p>
*
* @param number the char to convert in a HEX {@link String}.
* @return a <b>non-null</b> 4-characters {@link String} instance.
*/
public static String toHexString(char number) {
char output[] = new char[4];
output[0] = DAVUtilities.toHexDigit((int)(number >> 12));
output[1] = DAVUtilities.toHexDigit((int)(number >> 8));
output[2] = DAVUtilities.toHexDigit((int)(number >> 4));
output[3] = DAVUtilities.toHexDigit((int)(number));
return new String(output);
}
/**
* <p>Return the HEX representation of a byte.</p>
*
* @param number the byte to convert in a HEX {@link String}.
* @return a <b>non-null</b> 2-characters {@link String} instance.
*/
public static String toHexString(byte number) {
char output[] = new char[2];
output[0] = DAVUtilities.toHexDigit((int)(number >> 4));
output[1] = DAVUtilities.toHexDigit((int)(number));
return new String(output);
}
/**
* <p>Return the single digit character representing the HEX encoding of
* the lower four bits of a given integer.</p>
*/
private static char toHexDigit(int number) {
switch (number & 0x0F) {
case 0x00: return '0';
case 0x01: return '1';
case 0x02: return '2';
case 0x03: return '3';
case 0x04: return '4';
case 0x05: return '5';
case 0x06: return '6';
case 0x07: return '7';
case 0x08: return '8';
case 0x09: return '9';
case 0x0A: return 'A';
case 0x0B: return 'B';
case 0x0C: return 'C';
case 0x0D: return 'D';
case 0x0E: return 'E';
case 0x0F: return 'F';
}
String message = "Invalid HEX digit " + Integer.toHexString(number);
throw new IllegalArgumentException(message);
}
}

View File

@ -1,113 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.net.URI;
/**
* <p>A {@link DAVRepository} instance enforcing all {@link DAVResource}s to
* be XML files.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class XMLRepository extends DAVRepository {
/**
* <p>Create a new {@link XMLRepository} instance.</p>
*/
public XMLRepository(File root)
throws IOException {
super(root);
}
/**
* <p>Return the {@link DAVResource} associated with a {@link URI}.</p>
*/
public DAVResource getResource(URI uri)
throws IOException {
return new XMLResource(this, super.getResource(uri));
}
/**
* <p>A simple {@link DAVResource} extension enforcing XML writes.</p>
*/
private static final class XMLResource extends DAVResource {
/**
* <p>Create a new {@link XMLResource} instance.</p>
*/
public XMLResource(XMLRepository repository, DAVResource resource) {
super(repository, resource.getFile());
}
/**
* <p>Override the MIME Content-Type to <code>text/xml</code> for
* normal resources.</p>
*/
public String getContentType() {
if (this.isResource()) return "text/xml";
return super.getContentType();
}
/**
* <p>Return a {@link DAVOutputStream} enforcing XML formatted data.</p>
*/
public DAVOutputStream write() {
return new XMLOutputStream(this);
}
}
/**
* <p>A simple {@link DAVOutputStream} enforcing XML formatted data.</p>
*/
private static final class XMLOutputStream extends DAVOutputStream {
/**
* <p>Create a new {@link XMLOutputStream} instance.</p>
*/
protected XMLOutputStream(XMLResource resource) {
super(resource);
}
/**
* <p>Ensure that whatever is in the temporary file is XML.</p>
*/
protected void rename(File temporary, File original)
throws IOException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
SAXParser parser = factory.newSAXParser();
parser.parse(temporary, new DefaultHandler());
super.rename(temporary, original);
} catch (ParserConfigurationException exception) {
throw new DAVException(500, "JAXP parser error", exception);
} catch (SAXException exception) {
throw new DAVException(415, "Error parsing data", exception);
}
}
}
}

View File

@ -1,71 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVMultiStatus;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
import java.net.URI;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>COPY</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class COPY implements DAVMethod {
/**
* <p>Create a new {@link COPY} instance.</p>
*/
public COPY() {
super();
}
/**
* <p>Process the <code>COPY</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
URI target = transaction.getDestination();
if (target == null) throw new DAVException(412, "No destination");
DAVResource dest = resource.getRepository().getResource(target);
int depth = transaction.getDepth();
boolean recursive = false;
if (depth == 0) {
recursive = false;
} else if (depth == DAVTransaction.INFINITY) {
recursive = true;
} else {
throw new DAVException(412, "Invalid Depth specified");
}
try {
resource.copy(dest, transaction.getOverwrite(), recursive);
transaction.setStatus(transaction.getOverwrite() ? 204 : 201);
} catch (DAVMultiStatus multistatus) {
multistatus.write(transaction);
}
}
}

View File

@ -1,54 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVMultiStatus;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>DELETE</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DELETE implements DAVMethod {
/**
* <p>Create a new {@link DELETE} instance.</p>
*/
public DELETE() {
super();
}
/**
* <p>Process the <code>DELETE</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
try {
resource.delete();
transaction.setStatus(204);
} catch (DAVMultiStatus multistatus) {
multistatus.write(transaction);
}
}
}

View File

@ -1,144 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVInputStream;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a>
* <code>GET</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class GET extends HEAD {
/** <p>The encoding charset to repsesent collections.</p> */
public static final String ENCODING = "UTF-8";
/** <p>The mime type that {@link GET} will use serving collections.</p> */
public static final String COLLECTION_MIME_TYPE = "text/html ;charset=\""
+ ENCODING + "\"";
/**
* <p>Create a new {@link GET} instance.</p>
*/
public GET() {
super();
}
/**
* <p>Process the <code>GET</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
super.process(transaction, resource);
final String originalPath = transaction.getOriginalPath();
final String normalizedPath = transaction.getNormalizedPath();
final String current;
final String parent;
if (originalPath.equals(normalizedPath)) {
final String relativePath = resource.getRelativePath();
if (relativePath.equals("")) {
current = transaction.lookup(resource).toASCIIString();
} else {
current = relativePath;
}
parent = "./";
} else {
current = "./";
parent = "../";
}
if (resource.isCollection()) {
transaction.setHeader( "Content-Disposition", "inline; filename=\"index.html\"");
PrintWriter out = transaction.write(ENCODING);
String path = resource.getRelativePath();
out.println("<html>");
out.println("<head>");
out.println("<title>Collection: /" + path + "</title>");
out.println("</head>");
out.println("<body>");
out.println("<h2>Collection: /" + path + "</h2>");
out.println("<ul>");
/* Process the parent */
final DAVResource parentResource = resource.getParent();
if (parentResource != null) {
out.print("<li><a href=\"");
out.print(parent);
out.print("\">");
out.print(parentResource.getDisplayName());
out.println("</a> <i><small>(Parent)</small></i></li>");
out.println("</ul>");
out.println("<ul>");
}
/* Process the children (in two sorted sets, for nice ordering) */
Set resources = new TreeSet();
Set collections = new TreeSet();
Iterator iterator = resource.getChildren();
while (iterator.hasNext()) {
final DAVResource child = (DAVResource) iterator.next();
final StringBuffer buffer = new StringBuffer();
final String childPath = child.getDisplayName();
buffer.append("<li><a href=\"");
buffer.append(current);
buffer.append(childPath);
buffer.append("\">");
buffer.append(childPath);
buffer.append("</li>");
if (child.isCollection()) {
collections.add(buffer.toString());
} else {
resources.add(buffer.toString());
}
}
/* Spit out the collections first and the resources then */
for (Iterator i = collections.iterator(); i.hasNext(); )
out.println(i.next());
for (Iterator i = resources.iterator(); i.hasNext(); )
out.println(i.next());
out.println("</ul>");
out.println("</body>");
out.println("</html>");
out.flush();
return;
}
/* Processing a normal resource request */
OutputStream out = transaction.write();
DAVInputStream in = resource.read();
byte buffer[] = new byte[4096];
int k = -1;
while ((k = in.read(buffer)) != -1) out.write(buffer, 0, k);
in.close();
out.flush();
}
}

View File

@ -1,79 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVNotModified;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import it.could.webdav.DAVUtilities;
import java.io.IOException;
import java.util.Date;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a>
* <code>HEAD</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class HEAD implements DAVMethod {
/**
* <p>Create a new {@link HEAD} instance.</p>
*/
public HEAD() {
super();
}
/**
* <p>Process the <code>HEAD</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
/* Check if we have to force a resource not found or a redirection */
if (resource.isNull())
throw new DAVException(404, "Not found", resource);
/* Check if this is a conditional (processable only for resources) */
Date ifmod = transaction.getIfModifiedSince();
Date lsmod = resource.getLastModified();
if (resource.isResource() && (ifmod != null) && (lsmod != null)) {
/* HTTP doesn't send milliseconds, but Java does, so, reset them */
lsmod = new Date(((long)(lsmod.getTime() / 1000)) * 1000);
if (!ifmod.before(lsmod)) throw new DAVNotModified(resource);
}
/* Get the headers of this method */
String ctyp = resource.getContentType();
String etag = resource.getEntityTag();
String lmod = DAVUtilities.formatHttpDate(resource.getLastModified());
String clen = DAVUtilities.formatNumber(resource.getContentLength());
/* Set the normal headers that are required for a GET */
if (resource.isCollection()) {
transaction.setContentType(GET.COLLECTION_MIME_TYPE);
} else if (ctyp != null) {
transaction.setContentType(ctyp);
}
if (etag != null) transaction.setHeader("ETag", etag);
if (lmod != null) transaction.setHeader("Last-Modified", lmod);
if (clen != null) transaction.setHeader("Content-Length", clen);
}
}

View File

@ -1,57 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>MKCOL</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class MKCOL implements DAVMethod {
/**
* <p>Create a new {@link MKCOL} instance.</p>
*/
public MKCOL() {
super();
}
/**
* <p>Process the <code>MKCOL</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
/* Unsupported media type, we don't want content */
if (transaction.hasRequestBody()) {
throw new DAVException (415, "No request body allowed in request");
}
/* Create the collection */
resource.makeCollection();
transaction.setStatus(201);
}
}

View File

@ -1,80 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVMultiStatus;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
import java.net.URI;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>MOVE</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class MOVE implements DAVMethod {
/**
* <p>Create a new {@link MOVE} instance.</p>
*/
public MOVE() {
super();
}
/**
* <p>Process the <code>MOVE</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
URI target = transaction.getDestination();
if (target == null) throw new DAVException(412, "No destination");
DAVResource dest = resource.getRepository().getResource(target);
int depth = transaction.getDepth();
boolean recursive = false;
if (depth == 0) {
recursive = false;
} else if (depth == DAVTransaction.INFINITY) {
recursive = true;
} else {
throw new DAVException(412, "Invalid Depth specified");
}
try {
int status;
if(! dest.isNull() && ! transaction.getOverwrite()) {
status = 412; // MOVE-on-existing should fail with 412
} else {
resource.move(dest, transaction.getOverwrite(), recursive);
if(transaction.getOverwrite()) {
status = 204; // No Content
} else {
status = 201; // Created
}
}
transaction.setStatus(status);
} catch (DAVMultiStatus multistatus) {
multistatus.write(transaction);
}
}
}

View File

@ -1,52 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVProcessor;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a>
* <code>OPTIONS</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class OPTIONS implements DAVMethod {
/**
* <p>Create a new {@link OPTIONS} instance.</p>
*/
public OPTIONS() {
super();
}
/**
* <p>Process the <code>OPTIONS</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
transaction.setHeader("Content-Type", resource.getContentType());
transaction.setHeader("Allow", DAVProcessor.METHODS);
transaction.setStatus(200);
}
}

View File

@ -1,122 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import it.could.webdav.DAVUtilities;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>PROPFIND</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class PROPFIND implements DAVMethod {
/**
* <p>Create a new {@link PROPFIND} instance.</p>
*/
public PROPFIND() {
super();
}
/**
* <p>Process the <code>PROPFIND</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
/* Check if we have to force a resource not found or a redirection */
if (resource.isNull())
throw new DAVException(404, "Not found", resource);
/* Check depth */
int depth = transaction.getDepth();
if (depth > 1) new DAVException(403, "Invalid depth");
/* What to do on a collection resource */
transaction.setStatus(207);
transaction.setContentType("text/xml; charset=\"UTF-8\"");
PrintWriter out = transaction.write("UTF-8");
/* Output the XML declaration and the root document tag */
out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("<D:multistatus xmlns:D=\"DAV:\">");
/* Process this resource's property (always) */
this.process(transaction, out, resource);
/* Process this resource's children (if required) */
if (resource.isCollection() && (depth > 0)) {
Iterator children = resource.getChildren();
while (children.hasNext()) {
DAVResource child = (DAVResource) children.next();
this.process(transaction, out, child);
}
}
/* Close up the XML Multi-Status response */
out.println("</D:multistatus>");
out.flush();
}
private void process(DAVTransaction txn, PrintWriter out, DAVResource res) {
/* The href of the resource is only the absolute path */
out.println(" <D:response>");
out.println(" <D:href>" + txn.lookup(res).getPath() + "</D:href>");
out.println(" <D:propstat>");
out.println(" <D:prop>");
/* Figure out what we're dealing with here */
if (res.isCollection()) {
this.process(out, "resourcetype", "<D:collection/>");
}
this.process(out, "getcontenttype", res.getContentType());
this.process(out, "getetag", res.getEntityTag());
String date = DAVUtilities.formatIsoDate(res.getCreationDate());
this.process(out, "creationdate", date);
String lmod = DAVUtilities.formatHttpDate(res.getLastModified());
this.process(out, "getlastmodified", lmod);
String clen = DAVUtilities.formatNumber(res.getContentLength());
this.process(out, "getcontentlength", clen);
out.println(" </D:prop>");
out.println(" <D:status>HTTP/1.1 200 OK</D:status>");
out.println(" </D:propstat>");
out.println(" </D:response>");
}
private void process(PrintWriter out, String name, String value) {
if (value == null) return;
out.print(" <D:");
out.print(name);
out.print(">");
out.print(value);
out.print("</D:");
out.print(name);
out.println(">");
}
}

View File

@ -1,55 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>PROPPATCH</code> metohd implementation.</p>
*
* <p>As this servlet does not handle the creation of custom properties, this
* method will always fail with a <code>403</code> (Forbidden).</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class PROPPATCH implements DAVMethod {
/**
* <p>Create a new {@link PROPPATCH} instance.</p>
*/
public PROPPATCH() {
super();
}
/**
* <p>Process the <code>PROPPATCH</code> method.</p>
*
* <p>As this servlet does not handle the creation of custom properties,
* this method will always fail with a <code>403</code> (Forbidden).</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
throw new DAVException(403, "All properties are immutable");
}
}

View File

@ -1,72 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.methods;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVOutputStream;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
import java.io.InputStream;
/**
* <p><a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* <code>PUT</code> metohd implementation.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class PUT implements DAVMethod {
/**
* <p>Create a new {@link PUT} instance.</p>
*/
public PUT() {
super();
}
/**
* <p>Process the <code>PUT</code> method.</p>
*/
public void process(DAVTransaction transaction, DAVResource resource)
throws IOException {
/*
* The HTTP status code will depend on the existance of the resource:
* if not found: HTTP/1.1 201 Created
* if existing: HTTP/1.1 204 No Content
*/
transaction.setStatus(resource.isNull()? 201: 204);
/* Open the streams for reading and writing */
InputStream in = transaction.read();
if (in == null) throw new DAVException(411, "Content-Length required");
DAVOutputStream out = resource.write();
/* Write the content from the PUT to the specified resource */
try {
byte buffer[] = new byte[4096];
int k = -1;
while ((k = in.read(buffer)) != -1) out.write(buffer, 0, k);
in.close();
out.close();
} finally {
out.abort();
}
}
}

View File

@ -1,12 +0,0 @@
<html>
<head>
<title>Could.IT WebDAV Servlet</title>
</head>
<body>
<p>
This package contains the implementation of all Level 1
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
methods provided by this framework.
</p>
</body>
</html>

View File

@ -1,134 +0,0 @@
<html>
<head>
<title>Could.IT WebDAV Servlet</title>
</head>
<body>
<p>
This package contains a minimal
<a href="http://java.sun.com/products/servlet/">Servlet</a>
based implementation of the
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
specification.
</p>
<p>
This implementation does not in any way try to replace or extend the
<a href="http://jakarta.apache.org/slide/">Apache Slide</a>
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
implementation, but tries to provide a <i>very light</i> and <i>extremely
minimal</i> alternative to be used in those scenarios where space is
a constraint (the <code>.jar</code> file is less than 100 kylobites),
and advanced features are not required.
</p>
<p>
The most visible limitations of this approach is that this
implementation does not offer any support for the <code>LOCK</code>
method (it is therefore not <i>DAV Level 2</i> compliant), and that
there limited support for properties:
</p>
<ul>
<li>
The <code>PROPFIND</code> will only return the <i>read-only</i>
<code>getcontenttype</code>, <code>getlastmodified</code>,
<code>getcontentlength</code>, <code>getetag</code> and
<code>resourcetype</code> properties.
</li>
<li>
The <code>PROPPATCH</code> will <i>always</i> fail with a
<code>403</code> <i>Not Found</i> error.
</li>
</ul>
<p>
Another important limitation is that this implementation will only and
exclusively provide access to a {@link java.io.File} based backend.
If you want to deploy your repository on another kind of backend (such
as SQL databases) please look at the WebDAV implementation provided by
<a href="http://jakarta.apache.org/slide/">Apache Slide</a>.
</p>
<h2>Configuration</h2>
<p>
The main entry point of this implementation is defined in the
{@link it.could.webdav.DAVServlet} class, which will handle all
<a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP</a> and
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a> requests
for the URI path it is configured to handle.
</p>
<p>
To operate properly the {@link it.could.webdav.DAVServlet} class
must be configured in the web-application's <code>web.xml</code>
deployment descriptor. The relevant parts of a snippet of an example
configuration deployment descriptor might look like the following:
</p>
<pre>
&lt;servlet&gt;
&lt;servlet-name&gt;dav&lt;/servlet-name&gt;
&lt;servlet-class&gt;it.could.webdav.DAVServlet&lt;/servlet-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;rootPath&lt;/param-name&gt;
&lt;param-value&gt;dav&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;xmlOnly&lt;/param-name&gt;
&lt;param-value&gt;false&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;debugEnabled&lt;/param-name&gt;
&lt;param-value&gt;false&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
...
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;dav&lt;/servlet-name&gt;
&lt;url-pattern&gt;/dav/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</pre>
<p>
In this example the {@link it.could.webdav.DAVServlet} servlet
is configured with all parameters its parameters:
</p>
<dl>
<dt>rootPath</dt>
<dd>
<i>[required]</i> This parameter indicates the path of the root of the
repository.<br />
If the specified parameter represents a relative path, it will be
treated as a {@link javax.servlet.ServletContext#getResource(String)
ServletContext resource}.<br />
Note that if you choose to distribute your web application in a
<code>.war</code> archive, your container will have to expand it
before intializing the {@link javax.servlet.ServletContext} as this
this implementation <i>requires</i> a {@link java.io.File} based
repository.
</dd>
<dt>xmlOnly</dt>
<dd>
<i>[optional, default=&quot;<code>false</code>&quot;]</i> This parameter
will instruct the {@link it.could.webdav.DAVServlet} to create
a very specialized version of the repository accepting only
<a href="http://www.w3.org/TR/REC-xml/#sec-well-formed">well-formed
XML</a> resources and collections.<br />
Note that when set to <code>true</code> this implementation will rely
on the <a href="http://java.sun.com/xml/jaxp/">JAXP</a> specification
to access a XML parser used to verify the <code>PUT</code> content.
</dd>
<dt>debugEnabled</dt>
<dd>
<i>[optional, default=&quot;<code>false</code>&quot;]</i> This parameter
will instruct the {@link it.could.webdav.DAVServlet} to log
unimportant debugging information (such as the methods called by the
client) in the {@link javax.servlet.ServletContext#log(String) context
log}.
</dd>
</dl>
<p>
The configured {@link it.could.webdav.DAVServlet} will then have
to be mapped to a path, and in the example above, every request for
any URL beginning in <code>/dav/</code> will be handled by this
implementation, with a repository rooted in the <code>/dav/</code>
directory of the web application.
</p>
</body>
</html>

View File

@ -1,242 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.replication;
import it.could.util.StreamTools;
import it.could.util.http.WebDavClient;
import it.could.util.location.Location;
import it.could.webdav.DAVListener;
import it.could.webdav.DAVLogger;
import it.could.webdav.DAVRepository;
import it.could.webdav.DAVResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* <p>TODO: Document this class.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVReplica extends Thread implements DAVListener {
private static final int SYNCHRONIZE = -1;
private final DAVRepository repository;
private final DAVLogger logger;
private final Location location;
private final List actions = new ArrayList();
public DAVReplica(DAVRepository repository, Location location,
DAVLogger logger)
throws IOException {
this.location = new WebDavClient(location).getLocation();
this.repository = repository;
this.logger = logger;
this.start();
}
public void synchronize()
throws IOException {
this.logger.log("Scheduling full synchronization");
this.notify(this.repository.getResource((String)null), SYNCHRONIZE);
}
public void notify(DAVResource resource, int event) {
this.logger.debug("Event for \"" + resource.getRelativePath() + "\"");
if (resource.getRepository() != this.repository) return;
synchronized (this.actions) {
this.actions.add(new Action(resource, event));
this.actions.notify();
}
}
public void run() {
this.logger.debug("Starting background replica thread on " + location);
while (true) try {
final DAVReplica.Action array[];
synchronized(this.actions) {
try {
if (this.actions.isEmpty()) this.actions.wait();
final int s = this.actions.size();
array = (Action []) this.actions.toArray(new Action[s]);
this.actions.clear();
} catch (InterruptedException exception) {
this.logger.debug("Exiting background replica thread");
return;
}
}
for (int x = 0; x < array.length; x ++) try {
this.replicate(array[x]);
} catch (Throwable throwable) {
final String path = array[x].resource.getRelativePath();
final String message = "Error synchronizing resource " + path;
this.logger.log(message, throwable);
}
} catch (Throwable throwable) {
this.logger.log("Replica thread attempted suicide", throwable);
}
}
private void replicate(DAVReplica.Action action) {
final DAVResource resource = action.resource;
if (action.event == SYNCHRONIZE) {
this.synchronize(resource);
} else try {
final String path = resource.getParent().getRelativePath();
final Location location = this.location.resolve(path);
final WebDavClient client = new WebDavClient(location);
final String child = resource.getName();
switch(action.event) {
case RESOURCE_CREATED:
case RESOURCE_MODIFIED:
this.logger.debug("Putting resource " + path);
this.put(resource, client);
break;
case RESOURCE_REMOVED:
case COLLECTION_REMOVED:
this.logger.debug("Deleting resource " + path);
client.delete(child);
break;
case COLLECTION_CREATED:
this.logger.debug("Creating collection " + path);
client.mkcol(child);
break;
}
} catch (IOException exception) {
String message = "Error replicating " + resource.getRelativePath();
this.logger.log(message, exception);
}
}
private void put(DAVResource resource, WebDavClient client)
throws IOException {
final String name = resource.getName();
final long length = resource.getContentLength().longValue();
final OutputStream output = client.put(name, length);
final InputStream input = resource.read();
StreamTools.copy(input, output);
}
private void synchronize(DAVResource resource) {
/* Figure out the path of the resource */
final String path = resource.getRelativePath();
/* If it's a file or null, just skip the whole thing */
if (! resource.isCollection()) {
this.logger.log("Synchronization on non-collection " + path);
return;
}
/* Open a webdav client to the collection to synchronize */
this.logger.log("Synchronizing collection " + path);
final WebDavClient client;
try {
final Location location = this.location.resolve(path);
client = new WebDavClient(location);
} catch (IOException exception) {
this.logger.log("Error creating WebDAV client", exception);
return;
}
/* Create a list of all children from the DAV client */
final Set children = new HashSet();
for (Iterator iter = client.iterator(); iter.hasNext(); )
children.add(iter.next());
/* Process all resource children one by one and ensure they exist */
for (Iterator iter = resource.getChildren(); iter.hasNext(); ) {
final DAVResource child = (DAVResource) iter.next();
final String name = child.getName();
/* Remove this from the resources that will be removed later */
children.remove(name);
/* If the client doesn't have this child, add it to the replica */
if (! client.hasChild(name)) try {
if (child.isCollection()) {
this.logger.debug("Client doesn't have collection " + name);
client.mkcol(name);
this.synchronize(child);
} else {
this.logger.debug("Client doesn't have resource " + name);
this.put(child, client);
}
} catch (IOException exception) {
this.logger.log("Error creating new child " + name, exception);
/* If this child is a collection, it must be a collection on dav */
} else if (child.isCollection()) try {
if (!client.isCollection(name)) {
this.logger.debug("Recreating collection " + name);
client.delete(name).mkcol(name);
}
this.synchronize(child);
} catch (IOException exception) {
this.logger.log("Error creating collection " + name, exception);
/* Ok, the resource is a normal one, verify size and timestamp */
} else try {
final Date rlast = child.getLastModified();
final Date dlast = client.getLastModified(name);
if ((rlast != null) && (rlast.equals(dlast))) {
final Long rlen = child.getContentLength();
final long dlen = client.getContentLength(name);
if ((rlen == null) || (rlen.longValue() != dlen)) {
this.logger.debug("Resending resource " + name);
this.put(child, client.delete(name));
}
}
} catch (IOException exception) {
this.logger.log("Error resending resource " + name, exception);
}
}
/* Any other child that was not removed above, will go away now! */
for (Iterator iter = children.iterator(); iter.hasNext(); ) {
final String name = (String) iter.next();
try {
this.logger.debug("Removing leftovers " + name);
client.delete(name);
} catch (IOException exception) {
this.logger.log("Error removing left over " + name, exception);
}
}
}
private static final class Action {
final DAVResource resource;
final int event;
private Action(DAVResource resource, int event) {
this.resource = resource;
this.event = event;
}
}
}

View File

@ -1,131 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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 it.could.webdav.replication;
import it.could.util.location.Location;
import it.could.webdav.DAVListener;
import it.could.webdav.DAVLogger;
import it.could.webdav.DAVRepository;
import it.could.webdav.DAVServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
/**
* <p>The {@link DAVReplicator} class is a {@link DAVListener} replicating
* all content to the WebDAV repository specified at construction.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
*/
public class DAVReplicator extends HttpServlet {
/** <p>The {@link DAVReplica} instances managed by this.</p> */
private final List replicas = new ArrayList();
/**
* <p>Create a new {@link DAVServlet} instance.</p>
*/
public DAVReplicator() {
super();
}
/**
* <p>Initialize this {@link Servlet} instance.</p>
*
* <p>This servlet requires a couple of initialization parameters: the
* first one is &quot;<code>repository</code>&quot; indicating the name of
* the {@link DAVServlet} in the &quot;<code>web.xml</code>&quot; deployment
* descriptor whose repository should be replicated.</p>
*
* <p>The second required parameter &quot;<code>replicas</code>&quot;
* must contain a (whitespace separated list of) URL(s) where the original
* repository should be replicated to.</p>
*
* <p>Finally, when set to <code>true</code>, the optional parameter
* <code>debugEnabled</code> will enable logging of method invocation and
* events in the repository.</p>
*/
public void init(ServletConfig config)
throws ServletException {
/* Initialize the super, just in case, and remember the context */
super.init(config);
/* Setup logging */
boolean debug = "true".equals(config.getInitParameter("debugEnabled"));
DAVLogger logger = new DAVLogger(config, debug);
/* Try to retrieve the WebDAV repository from the servlet context */
final String repositoryName = config.getInitParameter("repository");
final DAVRepository repository;
if (repositoryName == null) {
throw new ServletException("Parameter \"rootPath\" not specified");
} else try {
final String key = DAVServlet.getRepositoryKey(repositoryName);
final ServletContext context = config.getServletContext();
repository = (DAVRepository) context.getAttribute(key);
if (repository == null)
throw new ServletException("Unable to access repository from " +
"servlet \"" + repository + "\"");
} catch (ClassCastException exception) {
final String message = "Class cast exception accessing repository";
throw new ServletException(message, exception);
}
/* Access the different WebDAV replicas */
final String replicas = config.getInitParameter("replicas");
if (replicas == null) {
throw new ServletException("Parameter \"replicas\" not specified");
}
try {
final StringTokenizer tokenizer = new StringTokenizer(replicas);
while (tokenizer.hasMoreTokens()) {
final Location location = Location.parse(tokenizer.nextToken());
final DAVReplica replica = new DAVReplica(repository, location,
logger);
logger.log("Added repository replica to \"" + location + "\"");
repository.addListener(replica);
this.replicas.add(replica);
replica.synchronize();
}
} catch (IOException exception) {
throw new ServletException("Error creating replica", exception);
}
/* Check that we have at least one replica in */
if (this.replicas.size() != 0) return;
throw new ServletException("No replicas specified for repository");
}
/**
* <p>Destroy {@link DAVServlet} instance interrupting all running
* {@link DAVReplica} instances.</p>
*/
public void destroy() {
for (Iterator iter = this.replicas.iterator(); iter.hasNext() ; ) {
((DAVReplica) iter.next()).interrupt();
}
}
}

View File

@ -1,12 +0,0 @@
<html>
<head>
<title>Could.IT WebDAV Servlet</title>
</head>
<body>
<p>
This package contains a framework for maintaining fully replicated
<a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
repositories.
</p>
</body>
</html>

View File

@ -1,159 +0,0 @@
/*
* 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.maven.archiva.webdav;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* AbstractDavServerComponent
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractDavServerComponent.java 6000 2007-03-04 22:01:49Z joakime $
*/
public abstract class AbstractDavServerComponent
implements DavServerComponent
{
private List listeners;
protected boolean useIndexHtml = false;
public AbstractDavServerComponent()
{
listeners = new ArrayList();
}
public void addListener( DavServerListener listener )
{
listeners.add( listener );
}
public void removeListener( DavServerListener listener )
{
listeners.remove( listener );
}
protected void triggerCollectionCreated( String resource )
{
Iterator it = listeners.iterator();
while ( it.hasNext() )
{
DavServerListener listener = (DavServerListener) it.next();
try
{
listener.serverCollectionCreated( this, resource );
}
catch ( Exception e )
{
/* ignore error */
}
}
}
protected void triggerCollectionRemoved( String resource )
{
Iterator it = listeners.iterator();
while ( it.hasNext() )
{
DavServerListener listener = (DavServerListener) it.next();
try
{
listener.serverCollectionRemoved( this, resource );
}
catch ( Exception e )
{
/* ignore error */
}
}
}
protected void triggerResourceCreated( String resource )
{
Iterator it = listeners.iterator();
while ( it.hasNext() )
{
DavServerListener listener = (DavServerListener) it.next();
try
{
listener.serverResourceCreated( this, resource );
}
catch ( Exception e )
{
/* ignore error */
}
}
}
protected void triggerResourceRemoved( String resource )
{
Iterator it = listeners.iterator();
while ( it.hasNext() )
{
DavServerListener listener = (DavServerListener) it.next();
try
{
listener.serverResourceRemoved( this, resource );
}
catch ( Exception e )
{
/* ignore error */
}
}
}
protected void triggerResourceModified( String resource )
{
Iterator it = listeners.iterator();
while ( it.hasNext() )
{
DavServerListener listener = (DavServerListener) it.next();
try
{
listener.serverResourceModified( this, resource );
}
catch ( Exception e )
{
/* ignore error */
}
}
}
public boolean hasResource( String resource )
{
File rootDir = getRootDirectory();
if ( rootDir == null )
{
return false;
}
File resourceFile = new File( rootDir, resource );
return resourceFile.exists();
}
public boolean isUseIndexHtml()
{
return this.useIndexHtml;
}
public void setUseIndexHtml( boolean useIndexHtml )
{
this.useIndexHtml = useIndexHtml;
}
}

View File

@ -0,0 +1,72 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.DavLocatorFactory;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.util.Text;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.maven.archiva.webdav.util.RepositoryPathUtil;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class ArchivaDavLocatorFactory implements DavLocatorFactory
{
public DavResourceLocator createResourceLocator(String prefix, String href)
{
// build prefix string and remove all prefixes from the given href.
StringBuilder b = new StringBuilder();
if (prefix != null && prefix.length() > 0) {
b.append(prefix);
if (!prefix.endsWith("/"))
{
b.append('/');
}
if (href.startsWith(prefix)) {
href = href.substring(prefix.length());
}
}
// special treatment for root item, that has no name but '/' path.
if (href == null || "".equals(href)) {
href = "/";
}
final String workspaceName = RepositoryPathUtil.getRepositoryName(href);
return new ArchivaDavResourceLocator(b.toString(), Text.unescape(href), workspaceName, this);
}
public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath)
{
return createResourceLocator(prefix, workspacePath, resourcePath, true);
}
public DavResourceLocator createResourceLocator(String prefix, String workspacePath,
String path, boolean isResourcePath)
{
final String repository = RepositoryPathUtil.getRepositoryName(path);
return new ArchivaDavResourceLocator(prefix, path, repository, this);
}
}

View File

@ -0,0 +1,301 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.*;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.lock.*;
import org.apache.jackrabbit.util.Text;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.apache.maven.archiva.webdav.util.MimeTypes;
import org.apache.maven.archiva.webdav.util.IndexWriter;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Date;
import java.io.*;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class ArchivaDavResource implements DavResource
{
private final MimeTypes mimeTypes;
private final DavResourceLocator locator;
private final DavResourceFactory factory;
private final DavSession session;
private final File localResource;
private final String logicalResource;
private static final String METHODS = "OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE";
private static final String COMPLIANCE_CLASS = "1";
private DavPropertySet properties;
public ArchivaDavResource(String localResource, String logicalResource, MimeTypes mimeTypes, DavResourceLocator locator, DavResourceFactory factory, DavSession session)
{
this.mimeTypes = mimeTypes;
this.localResource = new File(localResource);
this.logicalResource = logicalResource;
this.locator = locator;
this.factory = factory;
this.session = session;
this.properties = new DavPropertySet();
}
public String getContentType()
{
return mimeTypes.getMimeType(localResource.getName());
}
public String getComplianceClass()
{
return COMPLIANCE_CLASS;
}
public String getSupportedMethods()
{
return METHODS;
}
public boolean exists()
{
return localResource.exists();
}
public boolean isCollection()
{
return localResource.isDirectory();
}
public String getDisplayName()
{
String resPath = getResourcePath();
return (resPath != null) ? Text.getName(resPath) : resPath;
}
public DavResourceLocator getLocator()
{
return locator;
}
public String getResourcePath()
{
return locator.getResourcePath();
}
public String getHref()
{
return locator.getHref(isCollection());
}
public long getModificationTime()
{
return localResource.lastModified();
}
public long getContentLength()
{
return localResource.length();
}
public void spool(OutputContext outputContext) throws IOException
{
if (!isCollection())
{
IOUtils.copy(new FileInputStream(localResource), outputContext.getOutputStream());
}
else
{
IndexWriter writer = new IndexWriter(this, localResource, logicalResource);
writer.write(outputContext);
}
}
public DavPropertyName[] getPropertyNames()
{
return new DavPropertyName[0];
}
public DavProperty getProperty(DavPropertyName name)
{
return null;
}
public DavPropertySet getProperties()
{
return properties;
}
public void setProperty(DavProperty property) throws DavException
{
}
public void removeProperty(DavPropertyName propertyName) throws DavException
{
}
public MultiStatusResponse alterProperties(DavPropertySet setProperties, DavPropertyNameSet removePropertyNames) throws DavException
{
return null;
}
public MultiStatusResponse alterProperties(List changeList) throws DavException
{
return null;
}
public DavResource getCollection()
{
DavResource parent = null;
if (getResourcePath() != null && !getResourcePath().equals("/")) {
String parentPath = Text.getRelativeParent(getResourcePath(), 1);
if (parentPath.equals("")) {
parentPath = "/";
}
DavResourceLocator parentloc = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), parentPath);
try {
parent = factory.createResource(parentloc, session);
} catch (DavException e) {
// should not occur
}
}
return parent;
}
public void addMember(DavResource resource, InputContext inputContext) throws DavException
{
File localFile = new File(localResource, resource.getDisplayName());
if (!resource.isCollection() && isCollection() && inputContext.hasStream()) //New File
{
boolean deleteFile = false;
FileOutputStream stream = null;
try
{
stream = new FileOutputStream(localFile);
IOUtils.copy(inputContext.getInputStream(), stream);
if (inputContext.getContentLength() != localFile.length())
{
deleteFile = true;
throw new DavException(HttpServletResponse.SC_BAD_REQUEST, "Content Header length was "
+ inputContext.getContentLength() + " but was " + localFile.length());
}
}
catch (IOException e)
{
throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
finally
{
IOUtils.closeQuietly(stream);
if (deleteFile)
{
FileUtils.deleteQuietly(localFile);
}
}
}
else if (resource.isCollection() && isCollection()) //New directory
{
localFile.mkdir();
}
else
{
throw new DavException(HttpServletResponse.SC_BAD_REQUEST, "Could not write member "
+ resource.getResourcePath() + " at " + getResourcePath());
}
}
public DavResourceIterator getMembers()
{
return null;
}
public void removeMember(DavResource member) throws DavException
{
}
public void move(DavResource destination) throws DavException
{
}
public void copy(DavResource destination, boolean shallow) throws DavException
{
}
public boolean isLockable(Type type, Scope scope)
{
return false;
}
public boolean hasLock(Type type, Scope scope)
{
return false;
}
public ActiveLock getLock(Type type, Scope scope)
{
return null;
}
public ActiveLock[] getLocks()
{
return new ActiveLock[0];
}
public ActiveLock lock(LockInfo reqLockInfo) throws DavException
{
return null;
}
public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) throws DavException
{
return null;
}
public void unlock(String lockToken) throws DavException
{
}
public void addLockManager(LockManager lockmgr)
{
}
public DavResourceFactory getFactory()
{
return factory;
}
public DavSession getSession()
{
return session;
}
}

View File

@ -0,0 +1,497 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import com.opensymphony.xwork.ActionContext;
import org.apache.jackrabbit.webdav.*;
import org.apache.maven.archiva.repository.ManagedRepositoryContent;
import org.apache.maven.archiva.repository.RepositoryNotFoundException;
import org.apache.maven.archiva.repository.RepositoryException;
import org.apache.maven.archiva.repository.RepositoryContentFactory;
import org.apache.maven.archiva.repository.layout.LayoutException;
import org.apache.maven.archiva.repository.content.RepositoryRequest;
import org.apache.maven.archiva.repository.audit.AuditListener;
import org.apache.maven.archiva.repository.audit.Auditable;
import org.apache.maven.archiva.repository.audit.AuditEvent;
import org.apache.maven.archiva.repository.metadata.MetadataTools;
import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
import org.apache.maven.archiva.webdav.util.MimeTypes;
import org.apache.maven.archiva.webdav.util.RepositoryPathUtil;
import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
import org.apache.maven.archiva.common.utils.PathUtil;
import org.apache.maven.archiva.model.ArtifactReference;
import org.apache.maven.archiva.model.ProjectReference;
import org.apache.maven.archiva.model.VersionedReference;
import org.apache.maven.archiva.policies.ProxyDownloadException;
import org.apache.maven.archiva.security.ArchivaXworkUser;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Relocation;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.util.ArrayList;
import java.util.List;
import java.io.*;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
* @plexus.component role="org.apache.maven.archiva.webdav.ArchivaDavResourceFactory"
*/
public class ArchivaDavResourceFactory implements DavResourceFactory, Auditable
{
private Logger log = LoggerFactory.getLogger(ArchivaDavResourceFactory.class);
/**
* @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
*/
private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
/**
* @plexus.requirement
*/
private RepositoryContentFactory repositoryFactory;
/**
* @plexus.requirement
*/
private RepositoryRequest repositoryRequest;
/**
* @plexus.requirement role-hint="default"
*/
private RepositoryProxyConnectors connectors;
/**
* @plexus.requirement
*/
private MetadataTools metadataTools;
/**
* @plexus.requirement
*/
private MimeTypes mimeTypes;
public DavResource createResource(final DavResourceLocator locator, final DavServletRequest request, final DavServletResponse response) throws DavException
{
final ManagedRepositoryContent managedRepository = getManagedRepository(locator.getWorkspaceName());
final LogicalResource logicalResource = new LogicalResource(RepositoryPathUtil.getLogicalResource(locator.getResourcePath()));
DavResource resource = null;
if (managedRepository != null)
{
final boolean isGet = WebdavMethodUtil.isReadMethod( request.getMethod() );
final boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
if (isGet)
{
resource = doGet(managedRepository, request, locator, logicalResource);
}
if (isPut)
{
resource = doPut(managedRepository, request, locator, logicalResource);
}
}
else
{
throw new DavException(HttpServletResponse.SC_NOT_FOUND, "Repository does not exist");
}
if (resource != null)
{
setHeaders(locator, response);
return resource;
}
throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not get resource for method " + request.getMethod());
}
public DavResource createResource(final DavResourceLocator locator, final DavSession davSession) throws DavException
{
final ManagedRepositoryContent managedRepository = getManagedRepository(locator.getWorkspaceName());
final String logicalResource = RepositoryPathUtil.getLogicalResource(locator.getResourcePath());
final File resourceFile = new File ( managedRepository.getRepoRoot(), logicalResource);
return new ArchivaDavResource(resourceFile.getAbsolutePath(), logicalResource, mimeTypes, locator, this, null);
}
private DavResource doGet(ManagedRepositoryContent managedRepository, DavServletRequest request, DavResourceLocator locator, LogicalResource logicalResource) throws DavException
{
File resourceFile = new File ( managedRepository.getRepoRoot(), logicalResource.getPath());
ArchivaDavResource resource = new ArchivaDavResource(resourceFile.getAbsolutePath(), logicalResource.getPath(), mimeTypes, locator, this, null);
if ( !resource.isCollection() )
{
// At this point the incoming request can either be in default or
// legacy layout format.
try
{
boolean fromProxy = fetchContentFromProxies(managedRepository, request, logicalResource );
// Perform an adjustment of the resource to the managed
// repository expected path.
String localResourcePath = repositoryRequest.toNativePath( logicalResource.getPath(), managedRepository );
resourceFile = new File( managedRepository.getRepoRoot(), localResourcePath );
boolean previouslyExisted = resourceFile.exists();
// Attempt to fetch the resource from any defined proxy.
if ( fromProxy )
{
processAuditEvents(request, locator.getWorkspaceName(), logicalResource.getPath(), previouslyExisted, resourceFile, " (proxied)");
}
resource = new ArchivaDavResource(resourceFile.getAbsolutePath(), logicalResource.getPath(), mimeTypes, locator, this, null);
}
catch ( LayoutException e )
{
throw new DavException(HttpServletResponse.SC_NOT_FOUND, e);
}
}
return resource;
}
private DavResource doPut(ManagedRepositoryContent managedRepository, DavServletRequest request, DavResourceLocator locator, LogicalResource logicalResource) throws DavException
{
/*
* Create parent directories that don't exist when writing a file
* This actually makes this implementation not compliant to the
* WebDAV RFC - but we have enough knowledge about how the
* collection is being used to do this reasonably and some versions
* of Maven's WebDAV don't correctly create the collections
* themselves.
*/
File rootDirectory = new File(managedRepository.getRepoRoot());
File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile();
if ( !destDir.exists() )
{
destDir.mkdirs();
String relPath =
PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
triggerAuditEvent(request, logicalResource.getPath(), relPath, AuditEvent.CREATE_DIR );
}
File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
boolean previouslyExisted = resourceFile.exists();
processAuditEvents(request, locator.getWorkspaceName(), logicalResource.getPath(), previouslyExisted, resourceFile, null );
return new ArchivaDavResource(resourceFile.getAbsolutePath(), logicalResource.getPath(), mimeTypes, locator, this, null);
}
private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request, LogicalResource resource )
throws DavException
{
if ( repositoryRequest.isSupportFile( resource.getPath() ) )
{
// Checksums are fetched with artifact / metadata.
// Need to adjust the path for the checksum resource.
return false;
}
// Is it a Metadata resource?
if ( repositoryRequest.isDefault( resource.getPath() ) && repositoryRequest.isMetadata( resource.getPath() ) )
{
return fetchMetadataFromProxies(managedRepository, request, resource );
}
// Not any of the above? Then it's gotta be an artifact reference.
try
{
// Get the artifact reference in a layout neutral way.
ArtifactReference artifact = repositoryRequest.toArtifactReference( resource.getPath() );
if ( artifact != null )
{
applyServerSideRelocation(managedRepository, artifact );
File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
resource.setPath( managedRepository.toPath( artifact ) );
return ( proxiedFile != null );
}
}
catch ( LayoutException e )
{
/* eat it */
}
catch ( ProxyDownloadException e )
{
log.error(e.getMessage(), e);
throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to fetch artifact resource.");
}
return false;
}
private boolean fetchMetadataFromProxies(ManagedRepositoryContent managedRepository, DavServletRequest request, LogicalResource resource )
throws DavException
{
ProjectReference project;
VersionedReference versioned;
try
{
versioned = metadataTools.toVersionedReference( resource.getPath() );
if ( versioned != null )
{
connectors.fetchFromProxies( managedRepository, versioned );
return true;
}
}
catch ( RepositoryMetadataException e )
{
log.error(e.getMessage(), e);
}
try
{
project = metadataTools.toProjectReference( resource.getPath() );
if ( project != null )
{
connectors.fetchFromProxies( managedRepository, project );
return true;
}
}
catch ( RepositoryMetadataException e )
{
log.error(e.getMessage(), e);
}
return false;
}
/**
* A relocation capable client will request the POM prior to the artifact,
* and will then read meta-data and do client side relocation. A simplier
* client (like maven 1) will only request the artifact and not use the
* metadatas.
* <p>
* For such clients, archiva does server-side relocation by reading itself
* the &lt;relocation&gt; element in metadatas and serving the expected
* artifact.
*/
protected void applyServerSideRelocation( ManagedRepositoryContent managedRepository, ArtifactReference artifact )
throws ProxyDownloadException
{
if ( "pom".equals( artifact.getType() ) )
{
return;
}
// Build the artifact POM reference
ArtifactReference pomReference = new ArtifactReference();
pomReference.setGroupId( artifact.getGroupId() );
pomReference.setArtifactId( artifact.getArtifactId() );
pomReference.setVersion( artifact.getVersion() );
pomReference.setType( "pom" );
// Get the artifact POM from proxied repositories if needed
connectors.fetchFromProxies( managedRepository, pomReference );
// Open and read the POM from the managed repo
File pom = managedRepository.toFile( pomReference );
if ( !pom.exists() )
{
return;
}
try
{
Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
DistributionManagement dist = model.getDistributionManagement();
if ( dist != null )
{
Relocation relocation = dist.getRelocation();
if ( relocation != null )
{
// artifact is relocated : update the repositoryPath
if ( relocation.getGroupId() != null )
{
artifact.setGroupId( relocation.getGroupId() );
}
if ( relocation.getArtifactId() != null )
{
artifact.setArtifactId( relocation.getArtifactId() );
}
if ( relocation.getVersion() != null )
{
artifact.setVersion( relocation.getVersion() );
}
}
}
}
catch ( FileNotFoundException e )
{
// Artifact has no POM in repo : ignore
}
catch ( IOException e )
{
// Unable to read POM : ignore.
}
catch ( XmlPullParserException e )
{
// Invalid POM : ignore
}
}
private void processAuditEvents( DavServletRequest request, String repositoryId, String resource,
boolean previouslyExisted, File resourceFile, String suffix )
{
if ( suffix == null )
{
suffix = "";
}
// Process Create Audit Events.
if ( !previouslyExisted && resourceFile.exists() )
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, repositoryId, resource, AuditEvent.CREATE_FILE + suffix );
}
else if ( resourceFile.isDirectory() )
{
triggerAuditEvent( request, repositoryId, resource, AuditEvent.CREATE_DIR + suffix );
}
}
// Process Remove Audit Events.
else if ( previouslyExisted && !resourceFile.exists() )
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, repositoryId, resource, AuditEvent.REMOVE_FILE + suffix );
}
else if ( resourceFile.isDirectory() )
{
triggerAuditEvent( request, repositoryId, resource, AuditEvent.REMOVE_DIR + suffix );
}
}
// Process modify events.
else
{
if ( resourceFile.isFile() )
{
triggerAuditEvent( request, repositoryId, resource, AuditEvent.MODIFY_FILE + suffix );
}
}
}
private void triggerAuditEvent( String user, String remoteIP, String repositoryId, String resource, String action )
{
AuditEvent event = new AuditEvent( repositoryId, user, resource, action );
event.setRemoteIP( remoteIP );
for ( AuditListener listener : auditListeners )
{
listener.auditEvent( event );
}
}
private void triggerAuditEvent( DavServletRequest request, String repositoryId, String resource, String action )
{
triggerAuditEvent( ArchivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() ), getRemoteIP( request ), repositoryId, resource, action );
}
private String getRemoteIP( DavServletRequest request )
{
return request.getRemoteAddr();
}
public void addAuditListener( AuditListener listener )
{
this.auditListeners.add( listener );
}
public void clearAuditListeners()
{
this.auditListeners.clear();
}
public void removeAuditListener( AuditListener listener )
{
this.auditListeners.remove( listener );
}
private void setHeaders(DavResourceLocator locator, DavServletResponse response)
{
// [MRM-503] - Metadata file need Pragma:no-cache response
// header.
if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) )
{
response.addHeader( "Pragma", "no-cache" );
response.addHeader( "Cache-Control", "no-cache" );
}
// TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
}
private ManagedRepositoryContent getManagedRepository(String respositoryId) throws DavException
{
if (respositoryId != null)
{
try
{
return repositoryFactory.getManagedRepositoryContent(respositoryId);
}
catch (RepositoryNotFoundException e)
{
throw new DavException(HttpServletResponse.SC_NOT_FOUND, e);
}
catch (RepositoryException e)
{
throw new DavException(HttpServletResponse.SC_NOT_FOUND, e);
}
}
return null;
}
class LogicalResource
{
private String path;
public LogicalResource(String path)
{
this.path = path;
}
public String getPath()
{
return path;
}
public void setPath(String path)
{
this.path = path;
}
}
}

View File

@ -0,0 +1,136 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavLocatorFactory;
import org.apache.jackrabbit.util.Text;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class ArchivaDavResourceLocator implements DavResourceLocator
{
private String prefix;
private String resourcePath;
private String href;
private String workspaceName;
private DavLocatorFactory davLocatorFactory;
public ArchivaDavResourceLocator(String prefix, String resourcePath, String workspaceName, DavLocatorFactory davLocatorFactory)
{
this.prefix = prefix;
this.workspaceName = workspaceName;
this.davLocatorFactory = davLocatorFactory;
// remove trailing '/' that is not part of the resourcePath except for the root item.
if (resourcePath.endsWith("/") && !"/".equals(resourcePath)) {
resourcePath = resourcePath.substring(0, resourcePath.length()-1);
}
this.resourcePath = resourcePath;
href = prefix + Text.escapePath(resourcePath);
}
public String getPrefix()
{
return prefix;
}
public String getResourcePath()
{
return resourcePath;
}
public String getWorkspacePath()
{
return "";
}
public String getWorkspaceName()
{
return workspaceName;
}
public boolean isSameWorkspace(DavResourceLocator locator)
{
return isSameWorkspace(locator.getWorkspaceName());
}
public boolean isSameWorkspace(String workspaceName)
{
return getWorkspaceName().equals(workspaceName);
}
public String getHref(boolean isCollection)
{
// avoid doubled trailing '/' for the root item
String suffix = (isCollection && !isRootLocation()) ? "/" : "";
return href + suffix;
}
public boolean isRootLocation()
{
return "/".equals(resourcePath);
}
public DavLocatorFactory getFactory()
{
return davLocatorFactory;
}
public String getRepositoryPath()
{
return getResourcePath();
}
/**
* Computes the hash code from the href, which is built using the final
* fields prefix and resourcePath.
*
* @return the hash code
*/
public int hashCode()
{
return href.hashCode();
}
/**
* Equality of path is achieved if the specified object is a <code>DavResourceLocator</code>
* object with the same hash code.
*
* @param obj the object to compare to
* @return <code>true</code> if the 2 objects are equal;
* <code>false</code> otherwise
*/
public boolean equals(Object obj)
{
if (obj instanceof DavResourceLocator)
{
DavResourceLocator other = (DavResourceLocator) obj;
return hashCode() == other.hashCode();
}
return false;
}
}

View File

@ -0,0 +1,53 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.DavSession;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class ArchivaDavSession implements DavSession
{
public void addReference(Object o)
{
throw new UnsupportedOperationException("No yet implemented.");
}
public void removeReference(Object o)
{
throw new UnsupportedOperationException("No yet implemented.");
}
public void addLockToken(String s)
{
throw new UnsupportedOperationException("No yet implemented.");
}
public String[] getLockTokens()
{
throw new UnsupportedOperationException("No yet implemented.");
}
public void removeLockToken(String s)
{
throw new UnsupportedOperationException("No yet implemented.");
}
}

View File

@ -0,0 +1,154 @@
package org.apache.maven.archiva.webdav;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.DavSessionProvider;
import org.apache.jackrabbit.webdav.WebdavRequest;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavServletRequest;
import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
import org.apache.maven.archiva.webdav.util.RepositoryPathUtil;
import org.apache.maven.archiva.security.ArchivaRoleConstants;
import org.codehaus.plexus.redback.xwork.filter.authentication.HttpAuthenticator;
import org.codehaus.plexus.redback.authentication.AuthenticationResult;
import org.codehaus.plexus.redback.authentication.AuthenticationException;
import org.codehaus.plexus.redback.system.SecuritySystem;
import org.codehaus.plexus.redback.system.SecuritySession;
import org.codehaus.plexus.redback.policy.MustChangePasswordException;
import org.codehaus.plexus.redback.policy.AccountLockedException;
import org.codehaus.plexus.redback.authorization.AuthorizationResult;
import org.codehaus.plexus.redback.authorization.AuthorizationException;
import org.codehaus.plexus.spring.PlexusToSpringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class ArchivaDavSessionProvider implements DavSessionProvider
{
private Logger log = LoggerFactory.getLogger(ArchivaDavSessionProvider.class);
private SecuritySystem securitySystem;
private HttpAuthenticator httpAuth;
public ArchivaDavSessionProvider(WebApplicationContext applicationContext)
{
securitySystem = (SecuritySystem) applicationContext.getBean( PlexusToSpringUtils.buildSpringId( SecuritySystem.ROLE ) );
httpAuth = (HttpAuthenticator) applicationContext.getBean( PlexusToSpringUtils.buildSpringId( HttpAuthenticator.ROLE, "basic" ) );
}
public boolean attachSession(WebdavRequest request) throws DavException
{
final String repositoryId = RepositoryPathUtil.getRepositoryName(removeContextPath(request));
return isAuthenticated(request, repositoryId) && isAuthorized(request, repositoryId);
}
public void releaseSession(WebdavRequest webdavRequest)
{
}
protected boolean isAuthenticated( WebdavRequest request, String repositoryId )
throws DavException
{
// Authentication Tests.
try
{
AuthenticationResult result = httpAuth.getAuthenticationResult(request, null);
if ( result == null || !result.isAuthenticated() )
{
//Unfortunatly, the DavSessionProvider does not pass in the response
httpAuth.authenticate(request, null);
}
}
catch ( AuthenticationException e )
{
throw new UnauthorizedDavException(repositoryId, "You are not authenticated");
}
catch ( AccountLockedException e )
{
throw new UnauthorizedDavException(repositoryId, "User account is locked.");
}
catch ( MustChangePasswordException e )
{
throw new UnauthorizedDavException(repositoryId, "You must change your password.");
}
return true;
}
protected boolean isAuthorized( WebdavRequest request, String repositoryId )
throws DavException
{
// Authorization Tests.
final boolean isWriteRequest = WebdavMethodUtil.isWriteMethod( request.getMethod() );
SecuritySession securitySession = httpAuth.getSecuritySession();
try
{
String permission = ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS;
if ( isWriteRequest )
{
permission = ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD;
}
//DavServletRequestInfo requestInfo = new DavServletRequestInfo(request);
AuthorizationResult authzResult =
securitySystem.authorize( securitySession, permission, repositoryId);
if ( !authzResult.isAuthorized() )
{
if ( authzResult.getException() != null )
{
log.info( "Authorization Denied [ip=" + request.getRemoteAddr() + ",isWriteRequest=" + isWriteRequest +
",permission=" + permission + ",repo=" + repositoryId + "] : " +
authzResult.getException().getMessage() );
}
throw new DavException(HttpServletResponse.SC_UNAUTHORIZED, "Access denied for repository " + repositoryId);
}
}
catch ( AuthorizationException e )
{
log.error(e.getMessage(), e);
throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Fatal Authorization Subsystem Error." );
}
return true;
}
private String removeContextPath(final DavServletRequest request)
{
String path = request.getRequestURI();
String ctx = request.getContextPath();
if (path.startsWith(ctx)) {
path = path.substring(ctx.length());
}
return path;
}
}

View File

@ -1,143 +0,0 @@
/*
* 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.maven.archiva.webdav;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
/**
* DavServerComponent
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DavServerComponent.java 6000 2007-03-04 22:01:49Z joakime $
*/
public interface DavServerComponent
{
/** The Plexus ROLE name */
public static final String ROLE = DavServerComponent.class.getName();
/**
* Get the Prefix for this server component.
* @return the prefix associated with this component.
*/
public String getPrefix();
/**
* Set the prefix for this server component.
* @param prefix the prefix to use.
*/
public void setPrefix( String prefix );
/**
* <p>
* Flag to indicate how the dav server component should treat a GET request against
* a DAV Collection.
* </p>
*
* <p>
* If true, the collection being requested will be searched for an index.html (or index.htm)
* file to serve back, before it defaults to displaying the collection (directory) contents.
* </p>
*
* <p>
* If false, the collection will always be presented in as a list of contents.
* </p>
*
* @return true to use the index.html instead of directory contents.
*/
public boolean isUseIndexHtml();
/**
* <p>
* Flag to indicate how the dav server component should treat a GET request against
* a DAV Collection.
* </p>
*
* <p>
* If true, the collection being requested will be searched for an index.html (or index.htm)
* file to serve back, before it defaults to displaying the collection (directory) contents.
* </p>
*
* <p>
* If false, the collection will always be presented in as a list of contents.
* </p>
*
* @param useIndexHtml true to use the index.html instead of directory contents.
*/
public void setUseIndexHtml( boolean useIndexHtml );
/**
* Get the root directory for this server.
*
* @return the root directory for this server.
*/
public File getRootDirectory();
/**
* Set the root directory for this server's content.
*
* @param rootDirectory the root directory for this server's content.
*/
public void setRootDirectory( File rootDirectory );
/**
* Add a Server Listener for this server component.
*
* @param listener the listener to add for this component.
*/
public void addListener( DavServerListener listener );
/**
* Remove a server listener for this server component.
*
* @param listener the listener to remove.
*/
public void removeListener( DavServerListener listener );
/**
* Perform any initialization needed.
*
* @param servletConfig the servlet config that might be needed.
* @throws DavServerException if there was a problem initializing the server component.
*/
public void init( ServletConfig servletConfig ) throws DavServerException;
/**
* Performs a simple filesystem check for the specified resource.
*
* @param resource the resource to check for.
* @return true if the resource exists.
*/
public boolean hasResource( String resource );
/**
* Process incoming request.
*
* @param request the incoming request to process.
* @param response the outgoing response to provide.
*/
public void process( DavServerRequest request, HttpServletResponse response )
throws DavServerException, ServletException, IOException;
}

View File

@ -1,39 +0,0 @@
/*
* 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.maven.archiva.webdav;
/**
* DavServerListener
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DavServerListener.java 5379 2007-01-07 22:54:41Z joakime $
*/
public interface DavServerListener
{
public void serverCollectionCreated( DavServerComponent server, String resource );
public void serverCollectionRemoved( DavServerComponent server, String resource );
public void serverResourceCreated( DavServerComponent server, String resource );
public void serverResourceRemoved( DavServerComponent server, String resource );
public void serverResourceModified( DavServerComponent server, String resource );
}

View File

@ -1,74 +0,0 @@
/*
* 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.maven.archiva.webdav;
import java.io.File;
import java.util.Collection;
/**
* DavServerManager
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DavServerManager.java 6017 2007-03-06 00:39:53Z joakime $
*/
public interface DavServerManager
{
/** The Plexus ROLE name. */
public static final String ROLE = DavServerManager.class.getName();
/**
* Create a DavServerComponent and start tracking it.
*
* @param prefix the prefix for this component.
* @param rootDirectory the root directory for this component's content. null to not set a root directory.
* @return the created component, suitable for use.
* @throws DavServerException
*/
public DavServerComponent createServer( String prefix, File rootDirectory ) throws DavServerException;
/**
* Get the collection of tracked servers.
*
* @return Collection of {@link DavServerComponent} objects.
*/
public Collection getServers();
/**
* Removes a specific server from the tracked list of servers.
*
* NOTE: This does not remove the associated files on disk, merely the reference being tracked.
*
* @param prefix the prefix to remove.
*/
public void removeServer( String prefix );
/**
* Get the {@link DavServerComponent} associated with the specified prefix.
*
* @param prefix the prefix for the dav server component to use.
* @return the DavServerComponent, or null if not found.
*/
public DavServerComponent getServer( String prefix );
/**
* Remove all servers being tracked by the manager.
*/
public void removeAllServers();
}

View File

@ -1,88 +0,0 @@
/*
* 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.maven.archiva.webdav;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* DefaultDavServerManager
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DefaultDavServerManager.java 7009 2007-10-25 23:34:43Z joakime $
*
* @plexus.component role="org.apache.maven.archiva.webdav.DavServerManager" role-hint="default"
*/
public class DefaultDavServerManager
implements DavServerManager
{
/**
* @plexus.requirement role-hint="simple"
*/
private DavServerComponent server;
private Map servers;
public DefaultDavServerManager()
{
servers = new HashMap();
}
public DavServerComponent createServer( String prefix, File rootDirectory )
throws DavServerException
{
if ( servers.containsKey( prefix ) )
{
throw new DavServerException( "Unable to create a new server on a pre-existing prefix [" + prefix + "]" );
}
server.setPrefix( prefix );
if ( rootDirectory != null )
{
server.setRootDirectory( rootDirectory );
}
servers.put( prefix, server );
return server;
}
public DavServerComponent getServer( String prefix )
{
return (DavServerComponent) servers.get( prefix );
}
public void removeServer( String prefix )
{
servers.remove( prefix );
}
public Collection getServers()
{
return servers.values();
}
public void removeAllServers()
{
servers.clear();
}
}

View File

@ -1,3 +1,5 @@
package org.apache.maven.archiva.webdav;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -7,7 +9,7 @@
* "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
* 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
@ -17,23 +19,20 @@
* under the License.
*/
package org.apache.maven.archiva.webdav.servlet;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
/**
* DavServerRequest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DavServerRequest.java 7073 2007-11-22 04:04:50Z brett $
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public interface DavServerRequest
public class LogicalResource
{
public String getPrefix();
private String path;
public String getLogicalResource();
public String getPath()
{
return path;
}
public void setLogicalResource( String logicalResource );
public WrappedRepositoryRequest getRequest();
public void setPath(String path)
{
this.path = path;
}
}

View File

@ -1,3 +1,5 @@
package org.apache.maven.archiva.webdav;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -7,7 +9,7 @@
* "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
* 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
@ -17,35 +19,25 @@
* under the License.
*/
package org.apache.maven.archiva.webdav;
import org.apache.jackrabbit.webdav.DavException;
import javax.servlet.http.HttpServletResponse;
/**
* DavServerException
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: DavServerException.java 5379 2007-01-07 22:54:41Z joakime $
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class DavServerException
extends Exception
public class UnauthorizedDavException extends DavException
{
final private String repositoryName;
public DavServerException()
public UnauthorizedDavException(String repositoryName, String message)
{
super(HttpServletResponse.SC_UNAUTHORIZED, message);
this.repositoryName = repositoryName;
}
public DavServerException( String message )
public String getRepositoryName()
{
super( message );
return repositoryName;
}
public DavServerException( Throwable cause )
{
super( cause );
}
public DavServerException( String message, Throwable cause )
{
super( message, cause );
}
}

View File

@ -1,164 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.webdav.DavServerManager;
import org.codehaus.plexus.spring.PlexusToSpringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
* AbstractWebDavServlet
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractWebDavServlet.java 7009 2007-10-25 23:34:43Z joakime $
*/
public abstract class AbstractWebDavServlet
extends HttpServlet
{
public static final String INIT_USE_INDEX_HTML = "dav.use.index.html";
private boolean debug = false;
protected DavServerManager davManager;
public String getServletInfo()
{
return "Plexus WebDAV Servlet";
}
public void init( ServletConfig config )
throws ServletException
{
super.init( config );
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( config.getServletContext() );
davManager = (DavServerManager) wac.getBean( PlexusToSpringUtils.buildSpringId( DavServerManager.ROLE ) );
if ( davManager == null )
{
throw new ServletException( "Unable to lookup davManager" );
}
}
/**
* Perform any authentication steps here.
*
* If authentication fails, it is the responsibility of the implementor to issue
* the appropriate status codes and/or challenge back on the response object, then
* return false on the overridden version of this method.
*
* To effectively not have authentication, just implement this method and always
* return true.
*
* @param davRequest the incoming dav request.
* @param httpResponse the outgoing http response.
* @return true if user is authenticated, false if not.
* @throws ServletException if there was a problem performing authencation.
* @throws IOException if there was a problem obtaining credentials or issuing challenge.
*/
public boolean isAuthenticated( DavServerRequest davRequest, HttpServletResponse httpResponse )
throws ServletException, IOException
{
// Always return true. Effectively no Authentication done.
return true;
}
/**
* Perform any authorization steps here.
*
* If authorization fails, it is the responsibility of the implementor to issue
* the appropriate status codes and/or challenge back on the response object, then
* return false on the overridden version of this method.
*
* to effectively not have authorization, just implement this method and always
* return true.
*
* @param davRequest
* @param httpResponse
* @return
* @throws ServletException
* @throws IOException
*/
public boolean isAuthorized( DavServerRequest davRequest, HttpServletResponse httpResponse )
throws ServletException, IOException
{
// Always return true. Effectively no Authorization done.
return true;
}
public boolean isDebug()
{
return debug;
}
public void setDebug( boolean debug )
{
this.debug = debug;
}
protected void requestDebug( HttpServletRequest request )
{
if ( debug )
{
System.out.println( "-->>> request ----------------------------------------------------------" );
System.out.println( "--> " + request.getScheme() + "://" + request.getServerName() + ":"
+ request.getServerPort() + request.getServletPath() );
System.out.println( request.getMethod() + " " + request.getRequestURI()
+ ( request.getQueryString() != null ? "?" + request.getQueryString() : "" ) + " " + "HTTP/1.1" );
Enumeration enHeaders = request.getHeaderNames();
while ( enHeaders.hasMoreElements() )
{
String headerName = (String) enHeaders.nextElement();
String headerValue = request.getHeader( headerName );
System.out.println( headerName + ": " + headerValue );
}
System.out.println();
System.out.println( "------------------------------------------------------------------------" );
}
}
public abstract void setUseIndexHtml( boolean useIndexHtml );
public boolean getUseIndexHtml( ServletConfig config )
throws ServletException
{
String useIndexHtml = config.getInitParameter( INIT_USE_INDEX_HTML );
if ( StringUtils.isEmpty( useIndexHtml ) )
{
return false;
}
return BooleanUtils.toBoolean( useIndexHtml );
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet.basic;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
/**
* BasicDavServerRequest - for requests that have a prefix based off of the servlet path id.
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: BasicDavServerRequest.java 7073 2007-11-22 04:04:50Z brett $
*/
public class BasicDavServerRequest
implements DavServerRequest
{
private WrappedRepositoryRequest request;
private String prefix;
private String logicalResource;
public BasicDavServerRequest( WrappedRepositoryRequest request )
{
this.request = request;
this.prefix = request.getServletPath();
this.logicalResource = request.getPathInfo();
}
public void setLogicalResource( String logicalResource )
{
this.logicalResource = logicalResource;
this.request.setPathInfo( logicalResource );
}
public String getLogicalResource()
{
return this.logicalResource;
}
public String getPrefix()
{
return this.prefix;
}
public WrappedRepositoryRequest getRequest()
{
return request;
}
}

View File

@ -1,142 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet.basic;
import org.apache.maven.archiva.webdav.DavServerComponent;
import org.apache.maven.archiva.webdav.DavServerException;
import org.apache.maven.archiva.webdav.servlet.AbstractWebDavServlet;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
import org.codehaus.plexus.util.StringUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
/**
* BasicWebDavServlet - Basic implementation of a single WebDAV server as servlet.
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: BasicWebDavServlet.java 6017 2007-03-06 00:39:53Z joakime $
*/
public class BasicWebDavServlet
extends AbstractWebDavServlet
{
public static final String INIT_ROOT_DIRECTORY = "dav.root";
private DavServerComponent davServer;
// -----------------------------------------------------------------------
// Servlet Implementation
// -----------------------------------------------------------------------
public void init( ServletConfig config )
throws ServletException
{
super.init( config );
String prefix = config.getServletName();
boolean useIndexHtml = getUseIndexHtml( config );
File rootDir = getRootDirectory( config );
if ( rootDir != null && !rootDir.isDirectory() )
{
log( "Invalid configuration, the dav root " + rootDir.getPath()
+ " is not a directory: [" + rootDir.getAbsolutePath() + "]" );
}
try
{
davServer = davManager.createServer( prefix, rootDir );
davServer.setUseIndexHtml( useIndexHtml );
davServer.init( config );
}
catch ( DavServerException e )
{
throw new ServletException( "Unable to create DAV Server component for prefix [" + prefix
+ "] mapped to root directory [" + rootDir.getPath() + "]", e );
}
}
public File getRootDirectory( ServletConfig config )
throws ServletException
{
String rootDirName = config.getInitParameter( INIT_ROOT_DIRECTORY );
if ( StringUtils.isEmpty( rootDirName ) )
{
log( "Init Parameter '" + INIT_ROOT_DIRECTORY + "' is empty." );
return null;
}
return new File( rootDirName );
}
protected void service( HttpServletRequest httpRequest, HttpServletResponse httpResponse )
throws ServletException, IOException
{
DavServerRequest davRequest = new BasicDavServerRequest( new WrappedRepositoryRequest( httpRequest ) );
if ( davServer == null )
{
throw new ServletException( "Unable to service DAV request due to unconfigured DavServerComponent." );
}
requestDebug( httpRequest );
if ( !isAuthenticated( davRequest, httpResponse ) )
{
return;
}
if ( !isAuthorized( davRequest, httpResponse ) )
{
return;
}
try
{
davServer.process( davRequest, httpResponse );
}
catch ( DavServerException e )
{
throw new ServletException( "Unable to process request.", e );
}
}
public void setUseIndexHtml( boolean useIndexHtml )
{
davServer.setUseIndexHtml( useIndexHtml );
}
public DavServerComponent getDavServer()
{
return davServer;
}
public void setDavServer( DavServerComponent davServer )
{
this.davServer = davServer;
}
}

View File

@ -1,123 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet.multiplexed;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
/**
* <p/>
* MultiplexedDavServerRequest - For requests that contain the server prefix information within the requested
* servlet's pathInfo parameter (as the first path entry).
* </p>
* <p/>
* <p/>
* You would use this dav server request object when you are working with a single servlet that is handling
* multiple dav server components.
* </p>
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: MultiplexedDavServerRequest.java 7073 2007-11-22 04:04:50Z brett $
*/
public class MultiplexedDavServerRequest
implements DavServerRequest
{
private WrappedRepositoryRequest request;
private String prefix;
private String logicalResource;
public MultiplexedDavServerRequest( WrappedRepositoryRequest request )
{
String requestPathInfo = StringUtils.defaultString( request.getPathInfo() );
// Remove prefixing slash as the repository id doesn't contain it;
if ( requestPathInfo.startsWith( "/" ) )
{
requestPathInfo = requestPathInfo.substring( 1 );
}
// Find first element, if slash exists.
int slash = requestPathInfo.indexOf( '/' );
if ( slash > 0 )
{
// Filtered: "central/org/apache/maven/" -> "central"
this.prefix = requestPathInfo.substring( 0, slash );
this.logicalResource = requestPathInfo.substring( slash );
if ( this.logicalResource.endsWith( "/.." ) )
{
this.logicalResource += "/";
}
/* Perform a simple security normalization of the requested pathinfo.
* This is to prevent requests for information outside of the root directory.
*/
this.logicalResource = FilenameUtils.normalize( logicalResource );
if ( logicalResource != null )
{
logicalResource = logicalResource.replace( '\\', '/' );
if ( logicalResource.startsWith( "//" ) )
{
logicalResource = logicalResource.substring( 1 );
}
}
else
{
this.logicalResource = "/";
}
}
else
{
this.prefix = requestPathInfo;
this.logicalResource = "/";
}
this.request = request;
this.request.setPathInfo( logicalResource );
}
public void setLogicalResource( String logicalResource )
{
this.logicalResource = logicalResource;
this.request.setPathInfo( logicalResource );
}
public String getLogicalResource()
{
return this.logicalResource;
}
public String getPrefix()
{
return this.prefix;
}
public WrappedRepositoryRequest getRequest()
{
return request;
}
}

View File

@ -1,137 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet.multiplexed;
import org.apache.maven.archiva.webdav.DavServerComponent;
import org.apache.maven.archiva.webdav.DavServerException;
import org.apache.maven.archiva.webdav.DavServerManager;
import org.apache.maven.archiva.webdav.servlet.AbstractWebDavServlet;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Iterator;
/**
* <p>
* MultiplexedWebDavServlet - and abstracted multiplexed webdav servlet.
* </p>
*
* <p>
* Implementations of this servlet should override the {@link #initServers} method and create all of the
* appropriate DavServerComponents needed using the {@link DavServerManager} obtained via the {@link #getDavManager()}
* method.
* </p>
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: MultiplexedWebDavServlet.java 6000 2007-03-04 22:01:49Z joakime $
*/
public abstract class MultiplexedWebDavServlet
extends AbstractWebDavServlet
{
private boolean useIndexHtml = false;
public void init( ServletConfig config )
throws ServletException
{
super.init( config );
this.useIndexHtml = getUseIndexHtml( config );
try
{
initServers( config );
}
catch ( DavServerException e )
{
throw new ServletException( e );
}
}
/**
* Create any DavServerComponents here.
* Use the {@link #createServer(String, File, ServletConfig)} method to create your servers.
*
* @param config the config to use.
* @throws DavServerException if there was a problem initializing the server components.
*/
public abstract void initServers( ServletConfig config )
throws DavServerException;
public DavServerComponent createServer( String prefix, File rootDirectory, ServletConfig config )
throws DavServerException
{
DavServerComponent serverComponent = davManager.createServer( prefix, rootDirectory );
serverComponent.setUseIndexHtml( useIndexHtml );
serverComponent.init( config );
return serverComponent;
}
protected void service( HttpServletRequest httpRequest, HttpServletResponse httpResponse )
throws ServletException, IOException
{
DavServerRequest davRequest = new MultiplexedDavServerRequest( new WrappedRepositoryRequest( httpRequest ) );
DavServerComponent davServer = davManager.getServer( davRequest.getPrefix() );
if ( davServer == null )
{
String errorMessage = "[" + davRequest.getPrefix() + "] Not Found (Likely Unconfigured).";
httpResponse.sendError( HttpURLConnection.HTTP_NOT_FOUND, errorMessage );
return;
}
requestDebug( httpRequest );
if ( !isAuthenticated( davRequest, httpResponse ) )
{
return;
}
if ( !isAuthorized( davRequest, httpResponse ) )
{
return;
}
try
{
davServer.process( davRequest, httpResponse );
}
catch ( DavServerException e )
{
throw new ServletException( "Unable to process request.", e );
}
}
public void setUseIndexHtml( boolean useIndexHtml )
{
for ( Iterator it = davManager.getServers().iterator(); it.hasNext(); )
{
DavServerComponent davServer = (DavServerComponent) it.next();
davServer.setUseIndexHtml( useIndexHtml );
}
}
}

View File

@ -1,130 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import it.could.webdav.DAVException;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVMultiStatus;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import java.io.IOException;
import java.net.URI;
/**
* HackedMoveMethod - Created to address the needs for inter-repository moves.
*
* @author Pier Fumagalli (Original it.could.webdav 0.4 version)
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> (Hacked Version)
* @version $Id: HackedMoveMethod.java 6000 2007-03-04 22:01:49Z joakime $
*/
public class HackedMoveMethod
implements DAVMethod
{
public HackedMoveMethod()
{
super();
}
/**
* <p>Process the <code>MOVE</code> method.</p>
*/
public void process( DAVTransaction transaction, DAVResource resource )
throws IOException
{
URI target = transaction.getDestination();
if ( target == null )
throw new DAVException( 412, "No destination" );
if ( target.getScheme() == null )
{
// This is a relative file system destination target.
DAVResource dest = resource.getRepository().getResource( target );
moveWithinRepository( transaction, resource, dest );
}
else
{
// This is a inter-repository move request.
URI dest = target;
moveInterRepository( transaction, resource, dest );
}
}
private void moveInterRepository( DAVTransaction transaction, DAVResource resource, URI dest )
throws DAVException
{
/* TODO: Figure out how to handle a Repository to Repository MOVE of content, and still maintain
* the security credentials from the original request. (Need to support NTLM, Digest, BASIC)
*
* IDEA: Could support non-secured Webdav Destination using slide client libraries.
*/
transaction.setStatus( 501 );
throw new DAVException( 501, "Server side MOVE to external WebDAV instance not supported." );
}
private void moveWithinRepository( DAVTransaction transaction, DAVResource resource, DAVResource dest )
throws IOException
{
int depth = transaction.getDepth();
boolean recursive = false;
if ( depth == 0 )
{
recursive = false;
}
else if ( depth == DAVTransaction.INFINITY )
{
recursive = true;
}
else
{
throw new DAVException( 412, "Invalid Depth specified" );
}
try
{
int status;
if ( !dest.isNull() && !transaction.getOverwrite() )
{
status = 412; // MOVE-on-existing should fail with 412
}
else
{
resource.copy( dest, transaction.getOverwrite(), recursive );
resource.delete();
if ( transaction.getOverwrite() )
{
status = 204; // No Content
}
else
{
status = 201; // Created
}
}
transaction.setStatus( status );
}
catch ( DAVMultiStatus multistatus )
{
multistatus.write( transaction );
}
}
}

View File

@ -1,303 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import it.could.util.StreamTools;
import it.could.webdav.DAVException;
import it.could.webdav.DAVInputStream;
import it.could.webdav.DAVMethod;
import it.could.webdav.DAVNotModified;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import it.could.webdav.DAVUtilities;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.webdav.util.MimeTypes;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* ReplacementGetMethod
*
* @author Pier Fumagalli (Original it.could.webdav 0.4 version)
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> (Replacement Version)
* @version $Id: ReplacementGetMethod.java 7002 2007-10-23 22:40:37Z joakime $
*
* @plexus.component
* role="it.could.webdav.DAVMethod"
* role-hint="get-with-indexing"
*/
public class ReplacementGetMethod implements DAVMethod
{
/** <p>The encoding charset to repsesent collections.</p> */
public static final String ENCODING = "UTF-8";
/** <p>The mime type that {@link ReplacementGetMethod} will use serving index.html files.</p> */
public static final String HTML_MIME_TYPE = "text/html";
/** <p>The mime type that {@link ReplacementGetMethod} will use serving collections.</p> */
public static final String COLLECTION_MIME_TYPE = HTML_MIME_TYPE + "; charset=\"" + ENCODING + "\"";
/** <p>The header for content disposition.</p> */
public static final String CONTENT_DISPOSITION = "Content-Disposition";
/** <p>The content-disposition for fancy-indexing.</p> */
public static final String INLINE_INDEX_HTML = "inline; filename=\"index.html\"";
/**
* @plexus.requirement
*/
private MimeTypes mimeTypes;
private boolean useIndexHtml = false;
/**
* <p>Create a new {@link ReplacementGetMethod} instance.</p>
*/
public ReplacementGetMethod()
{
super();
}
/**
* <p>Process the <code>GET</code> method.</p>
*/
public void process( DAVTransaction transaction, DAVResource resource ) throws IOException
{
// Handle boilerplate
if ( resource.isNull() )
throw new DAVException( 404, "Not found", resource );
notModified( transaction, resource );
copyHeaders( transaction, resource );
// Process the request.
final String originalPath = transaction.getOriginalPath();
final String normalizedPath = transaction.getNormalizedPath();
final String current;
final String parent;
if ( originalPath.equals( normalizedPath ) )
{
final String relativePath = resource.getRelativePath();
if ( relativePath.equals( "" ) )
{
current = transaction.lookup( resource ).toASCIIString();
}
else
{
current = relativePath;
}
parent = "./";
}
else
{
current = "./";
parent = "../";
}
if ( resource.isCollection() )
{
DAVResource indexHtml = null;
if ( useIndexHtml )
{
for ( Iterator it = resource.getChildren(); it.hasNext(); )
{
DAVResource child = (DAVResource) it.next();
String name = child.getDisplayName().toLowerCase();
if ( StringUtils.equals( "index.html", name ) || StringUtils.equals( "index.htm", name ) )
{
indexHtml = child;
break;
}
}
}
if ( useIndexHtml && indexHtml != null )
{
transaction.setContentType( COLLECTION_MIME_TYPE );
transaction.setHeader( CONTENT_DISPOSITION, INLINE_INDEX_HTML );
sendResource( transaction, indexHtml );
}
else
{
transaction.setContentType( COLLECTION_MIME_TYPE );
transaction.setHeader( CONTENT_DISPOSITION, INLINE_INDEX_HTML );
sendFancyIndex( transaction, resource, current, parent );
}
}
else
{
/* Processing a normal resource request */
transaction.setContentType( mimeTypes.getMimeType( resource.getDisplayName() ) );
transaction.setHeader( CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getDisplayName() + "\"" );
sendResource( transaction, resource );
}
}
private void copyHeaders( DAVTransaction transaction, DAVResource resource )
{
/* Get the headers of this method */
String ctyp = resource.getContentType();
String etag = resource.getEntityTag();
String lmod = DAVUtilities.formatHttpDate( resource.getLastModified() );
String clen = DAVUtilities.formatNumber( resource.getContentLength() );
/* Set the normal headers that are required for a GET */
if ( ctyp != null )
{
transaction.setContentType( ctyp );
}
if ( etag != null )
{
transaction.setHeader( "ETag", etag );
}
if ( lmod != null )
{
transaction.setHeader( "Last-Modified", lmod );
}
if ( clen != null )
{
transaction.setHeader( "Content-Length", clen );
}
}
private void sendResource( DAVTransaction transaction, DAVResource resource ) throws IOException
{
OutputStream out = null;
DAVInputStream in = null;
try
{
out = transaction.write();
in = resource.read();
byte buffer[] = new byte[4096 * 16];
int k = -1;
while ( ( k = in.read( buffer ) ) != -1 )
{
out.write( buffer, 0, k );
}
out.flush();
}
finally
{
StreamTools.close( in );
StreamTools.close( out );
}
}
private void sendFancyIndex( DAVTransaction transaction, DAVResource resource, final String current,
final String parent ) throws IOException
{
PrintWriter out = transaction.write( ENCODING );
String path = resource.getRelativePath();
out.println( "<html>" );
out.println( "<head>" );
out.println( "<title>Collection: /" + path + "</title>" );
out.println( "</head>" );
out.println( "<body>" );
out.println( "<h2>Collection: /" + path + "</h2>" );
out.println( "<ul>" );
/* Process the parent */
final DAVResource parentResource = resource.getParent();
if ( parentResource != null )
{
out.print( "<li><a href=\"" );
out.print( parent );
out.print( "\">" );
out.print( parentResource.getDisplayName() );
out.println( "</a> <i><small>(Parent)</small></i></li>" );
out.println( "</ul>" );
out.println( "<ul>" );
}
/* Process the children (in two sorted sets, for nice ordering) */
Set resources = new TreeSet();
Set collections = new TreeSet();
Iterator iterator = resource.getChildren();
while ( iterator.hasNext() )
{
final DAVResource child = (DAVResource) iterator.next();
final StringBuffer buffer = new StringBuffer();
final String childPath = child.getDisplayName();
buffer.append( "<li><a href=\"" );
buffer.append( current );
buffer.append( childPath );
buffer.append( "\">" );
buffer.append( childPath );
buffer.append( "</li>" );
if ( child.isCollection() )
{
collections.add( buffer.toString() );
}
else
{
resources.add( buffer.toString() );
}
}
/* Spit out the collections first and the resources then */
for ( Iterator i = collections.iterator(); i.hasNext(); )
out.println( i.next() );
for ( Iterator i = resources.iterator(); i.hasNext(); )
out.println( i.next() );
out.println( "</ul>" );
out.println( "</body>" );
out.println( "</html>" );
out.flush();
}
private void notModified( DAVTransaction transaction, DAVResource resource )
{
Date ifmod = transaction.getIfModifiedSince();
Date lsmod = resource.getLastModified();
if ( resource.isResource() && ( ifmod != null ) && ( lsmod != null ) )
{
/* HTTP doesn't send milliseconds, but Java does, so, reset them */
lsmod = new Date( ( (long) ( lsmod.getTime() / 1000 ) ) * 1000 );
if ( !ifmod.before( lsmod ) )
throw new DAVNotModified( resource );
}
}
public boolean isUseIndexHtml()
{
return useIndexHtml;
}
public void setUseIndexHtml( boolean useIndexHtml )
{
this.useIndexHtml = useIndexHtml;
}
}

View File

@ -1,185 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import it.could.webdav.DAVListener;
import it.could.webdav.DAVProcessor;
import it.could.webdav.DAVRepository;
import it.could.webdav.DAVResource;
import it.could.webdav.DAVTransaction;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.webdav.AbstractDavServerComponent;
import org.apache.maven.archiva.webdav.DavServerException;
import org.apache.maven.archiva.webdav.servlet.DavServerRequest;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
/**
* SimpleDavServerComponent
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: SimpleDavServerComponent.java 7097 2007-11-30 12:57:29Z handyande $
*
* @plexus.component role="org.apache.maven.archiva.webdav.DavServerComponent"
* role-hint="simple"
* instantiation-strategy="per-lookup"
*/
public class SimpleDavServerComponent
extends AbstractDavServerComponent
implements DAVListener
{
/**
* @plexus.requirement
* role="it.could.webdav.DAVMethod"
* role-hint="get-with-indexing"
*/
public ReplacementGetMethod methodGet;
private String prefix;
private File rootDirectory;
private DAVRepository davRepository;
private DAVProcessor davProcessor;
public String getPrefix()
{
return prefix;
}
public File getRootDirectory()
{
return rootDirectory;
}
public void setPrefix( String prefix )
{
this.prefix = prefix;
}
public void setRootDirectory( File rootDirectory )
{
this.rootDirectory = rootDirectory;
}
public void init( ServletConfig servletConfig )
throws DavServerException
{
servletConfig.getServletContext().log( "Initializing " + this.getClass().getName() );
try
{
davRepository = new DAVRepository( rootDirectory );
davProcessor = new DAVProcessor( davRepository );
davRepository.addListener( this );
hackDavProcessor( davProcessor );
}
catch ( IOException e )
{
throw new DavServerException( "Unable to initialize DAVRepository.", e );
}
}
/**
* Replace the problematic dav methods with local hacked versions.
*
* @param davProcessor
* @throws DavServerException
*/
private void hackDavProcessor( DAVProcessor davProcessor )
throws DavServerException
{
davProcessor.setMethod( "MOVE", new HackedMoveMethod() );
davProcessor.setMethod( "GET", methodGet );
/* Reflection based technique.
try
{
Field fldInstance = davProcessor.getClass().getDeclaredField( "INSTANCES" );
fldInstance.setAccessible( true );
Map mapInstances = (Map) fldInstance.get( davProcessor );
// Replace MOVE method.
// TODO: Remove MOVE method when upgrading it.could.webdav to v0.5
mapInstances.put( "MOVE", (DAVMethod) new HackedMoveMethod() );
// Replace GET method.
mapInstances.put( "GET", (DAVMethod) methodGet );
}
catch ( Throwable e )
{
throw new DavServerException( "Unable to twiddle DAVProcessor.INSTANCES field.", e );
}
*/
}
public void process( DavServerRequest request, HttpServletResponse response )
throws ServletException, IOException
{
DAVTransaction transaction = new DAVTransaction( request.getRequest(), response );
/* BEGIN - it.could.webdav hacks
* TODO: Remove hacks with release of it.could.webdav 0.5 (or newer)
*/
String depthValue = request.getRequest().getHeader( "Depth" );
if ( StringUtils.equalsIgnoreCase( "infinity", depthValue ) )
{
// See - http://could.it/bugs/browse/DAV-3
request.getRequest().setHeader( "Depth", "infinity" );
}
/* END - it.could.webdav hacks */
davProcessor.process( transaction );
}
public void notify( DAVResource resource, int event )
{
switch ( event )
{
case DAVListener.COLLECTION_CREATED:
triggerCollectionCreated( resource.getRelativePath() );
break;
case DAVListener.COLLECTION_REMOVED:
triggerCollectionRemoved( resource.getRelativePath() );
break;
case DAVListener.RESOURCE_CREATED:
triggerResourceCreated( resource.getRelativePath() );
break;
case DAVListener.RESOURCE_REMOVED:
triggerResourceRemoved( resource.getRelativePath() );
break;
case DAVListener.RESOURCE_MODIFIED:
triggerResourceModified( resource.getRelativePath() );
break;
}
}
public void setUseIndexHtml( boolean useIndexHtml )
{
super.setUseIndexHtml( useIndexHtml );
this.methodGet.setUseIndexHtml( useIndexHtml );
}
}

View File

@ -0,0 +1,111 @@
package org.apache.maven.archiva.webdav.util;
/*
* 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.
*/
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.io.OutputContext;
import java.util.Date;
import java.io.PrintWriter;
import java.io.File;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class IndexWriter
{
private final DavResource resource;
private final File localResource;
private final String logicalResource;
public IndexWriter(DavResource resource, File localResource, String logicalResource)
{
this.resource = resource;
this.localResource = localResource;
this.logicalResource = logicalResource;
}
public void write(OutputContext outputContext)
{
outputContext.setModificationTime(new Date().getTime());
outputContext.setContentType("text/html");
outputContext.setETag("");
if (outputContext.hasStream())
{
PrintWriter writer = new PrintWriter(outputContext.getOutputStream());
writeDocumentStart(writer);
writeHyperlinks(writer);
writeDocumentEnd(writer);
writer.flush();
writer.close();
}
}
private void writeDocumentStart(PrintWriter writer)
{
writer.println("<html>");
writer.println("<head>");
writer.println("<title>Collection: " + logicalResource + "<title>");
writer.println("</head>");
writer.println("<h3>Collection: " + logicalResource + "</h3>");
//Check if not root
if (!"/".equals(logicalResource))
{
File file = new File(logicalResource);
String parentName = file.getParent().equals("") ? "/" : file.getParent();
writer.println("<ul>");
writer.println("<li><a href=\"../\">" + parentName + "</a> <i><small>(Parent)</small></i></li>");
writer.println("</ul>");
}
writer.println("<ul>");
}
private void writeDocumentEnd(PrintWriter writer)
{
writer.println("</ul>");
writer.println("</body>");
writer.println("</html>");
}
private void writeHyperlinks(PrintWriter writer)
{
for (File file : localResource.listFiles())
{
writeHyperlink(writer, file.getName(), file.isDirectory());
}
}
private void writeHyperlink(PrintWriter writer, String resourceName, boolean directory)
{
if (directory)
{
writer.println("<li><a href=\"./" + resourceName + "/\">" + resourceName + "</a></li>");
}
else
{
writer.println("<li><a href=\"./" + resourceName + "\">" + resourceName + "</a></li>");
}
}
}

View File

@ -1,3 +1,5 @@
package org.apache.maven.archiva.webdav.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -7,7 +9,7 @@
* "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
* 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
@ -17,8 +19,6 @@
* under the License.
*/
package org.apache.maven.archiva.webdav.util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.plexus.logging.AbstractLogEnabled;
@ -49,10 +49,9 @@ public class MimeTypes
extends AbstractLogEnabled
implements Initializable
{
/**
* @plexus.configuration default-value="org/apache/maven/archiva/webdav/util/mime-types.txt"
*/
private String resource;
private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
private String resource = "org/apache/maven/archiva/webdav/util/mime.types";
private Map mimeMap = new HashMap();
@ -74,6 +73,13 @@ public class MimeTypes
value = (String) mimeMap.get( filename.substring( index + 1 ).toLowerCase() );
}
}
if (value == null)
{
value = DEFAULT_MIME_TYPE;
}
return value;
}

View File

@ -0,0 +1,105 @@
package org.apache.maven.archiva.webdav.util;
/*
* 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.
*/
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.io.FilenameUtils;
/**
* @author <a href="mailto:james@atlassian.com">James William Dumay</a>
*/
public class RepositoryPathUtil
{
public static String getLogicalResource(final String href)
{
String logicalResource = null;
String requestPathInfo = StringUtils.defaultString( href );
//remove prefix ie /repository/blah becomes /blah
requestPathInfo = removePrefix(requestPathInfo);
// Remove prefixing slash as the repository id doesn't contain it;
if ( requestPathInfo.startsWith( "/" ) )
{
requestPathInfo = requestPathInfo.substring( 1 );
}
int slash = requestPathInfo.indexOf( '/' );
if ( slash > 0 )
{
logicalResource = requestPathInfo.substring( slash );
if (logicalResource.endsWith( "/.." ) )
{
logicalResource += "/";
}
if ( logicalResource != null && logicalResource.startsWith( "//" ) )
{
logicalResource = logicalResource.substring( 1 );
}
if ( logicalResource == null )
{
logicalResource = "/";
}
}
else
{
logicalResource = "/";
}
return logicalResource;
}
public static String getRepositoryName(final String href)
{
String requestPathInfo = StringUtils.defaultString( href );
//remove prefix ie /repository/blah becomes /blah
requestPathInfo = removePrefix(requestPathInfo);
// Remove prefixing slash as the repository id doesn't contain it;
if ( requestPathInfo.startsWith( "/" ) )
{
requestPathInfo = requestPathInfo.substring( 1 );
}
// Find first element, if slash exists.
int slash = requestPathInfo.indexOf( '/' );
if ( slash > 0 )
{
// Filtered: "central/org/apache/maven/" -> "central"
return requestPathInfo.substring( 0, slash );
}
return requestPathInfo;
}
private static String removePrefix(final String href)
{
String[] parts = StringUtils.split(href, '/');
parts = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
if (parts == null || parts.length == 0)
{
return "/";
}
return StringUtils.join(parts, '/');
}
}

View File

@ -1,3 +1,5 @@
package org.apache.maven.archiva.webdav.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -7,7 +9,7 @@
* "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
* 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
@ -17,8 +19,6 @@
* under the License.
*/
package org.apache.maven.archiva.webdav.util;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;

View File

@ -1,184 +0,0 @@
/*
* 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.maven.archiva.webdav.util;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* RepositoryRequest - wrapped servlet request to adjust the incoming request before the components get it.
* It eliminates the prefix from the pathInfo portion of the URL requested.
* And also allows for Header adjustment.
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: WrappedRepositoryRequest.java 7001 2007-10-23 22:40:14Z joakime $
*/
public class WrappedRepositoryRequest
extends HttpServletRequestWrapper
{
private String pathInfo;
private Map headers;
/**
* The Date Formats most commonly seen in Request Headers.
*/
private SimpleDateFormat dateFormats[];
public WrappedRepositoryRequest( HttpServletRequest request )
{
super( request );
dateFormats = new SimpleDateFormat[] {
new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz" ),
new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss" ),
new SimpleDateFormat( "EEE MMM dd HH:mm:ss yyyy" ) };
headers = new HashMap();
Enumeration enHeaders = request.getHeaderNames();
while ( enHeaders.hasMoreElements() )
{
String name = (String) enHeaders.nextElement();
String value = request.getHeader( name );
headers.put( name, value );
}
}
public void setHeader( String name, String value )
{
headers.put( name, value );
}
public long getDateHeader( String name )
{
String value = (String) headers.get( name );
if ( StringUtils.isEmpty( value ) )
{
// no value? return -1
return -1;
}
// Try most common formats first.
for ( int i = 0; i < dateFormats.length; i++ )
{
try
{
Date date = (Date) dateFormats[i].parseObject( value );
return date.getTime();
}
catch ( java.lang.Exception e )
{
/* ignore exception */
}
}
// Now check for the odd "GMT" formats (hey, it happens)
if ( value.endsWith( " GMT" ) )
{
value = value.substring( 0, value.length() - 4 );
for ( int i = 0; i < dateFormats.length; i++ )
{
try
{
Date date = (Date) dateFormats[i].parseObject( value );
return date.getTime();
}
catch ( java.lang.Exception e )
{
/* ignore exception */
}
}
}
// unrecognized format? return -1
return -1;
}
public String getHeader( String name )
{
return (String) headers.get( name );
}
public Enumeration getHeaderNames()
{
return new Enumeration()
{
private Iterator iter = headers.keySet().iterator();
public boolean hasMoreElements()
{
return iter.hasNext();
}
public Object nextElement()
{
return iter.next();
}
};
}
public int getIntHeader( String name )
{
String value = getHeader( name );
try
{
return Integer.parseInt( value );
}
catch ( NumberFormatException e )
{
return -1;
}
}
public void setPathInfo( String alternatePathInfo )
{
this.pathInfo = alternatePathInfo;
}
public String getPathInfo()
{
if ( this.pathInfo != null )
{
return this.pathInfo;
}
return super.getPathInfo();
}
public String getServletPath()
{
if ( this.pathInfo != null )
{
return super.getServletPath() + "/" + this.pathInfo;
}
return super.getServletPath();
}
}

View File

@ -1,57 +0,0 @@
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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.betaversion.webdav;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* <p>The {@link DAVServlet} class has been moved to a new package and should
* now be referred as {@link it.could.webdav.DAVServlet}.</p>
*
* <p>This class will be preserved for some time (not so long) to give people
* time to update their servlet deployment descriptors.</p>
*
* @author <a href="http://could.it/">Pier Fumagalli</a>
* @deprecated This class has been moved into the <code>it.could.webdav</code>
* package. Reconfigure your <code>web.xml</code> deployment
* descriptor to use {@link it.could.webdav.DAVServlet}.
*/
public class DAVServlet extends it.could.webdav.DAVServlet {
/**
* <p>Create a new {@link DAVServlet} instance.</p>
*/
public DAVServlet() {
super();
}
/**
* <p>Initialize this {@link DAVServlet} instance reporting to the
* {@link ServletContext} log that this class is deprecated.</p>
*/
public void init(ServletConfig config)
throws ServletException {
final ServletContext context = config.getServletContext();
context.log("The class \"" + this.getClass().getName()
+ "\" is deprecated");
context.log("Modify the \"web.xml\" deployment descriptor to use \""
+ it.could.webdav.DAVServlet.class.getName() + "\"");
super.init(config);
}
}

View File

@ -1,15 +0,0 @@
<html>
<head>
<title>Could.IT WebDAV Servlet</title>
</head>
<body>
<p>
This package is deprecated, but preserved to maintain compatibility
with previous versions.
</p>
<p>
Please refer to the documentation in the {@link it.could.webdav} package
for the new version description.
</p>
</body>
</html>

View File

@ -1,128 +0,0 @@
# This is a comment. I love comments.
# This file controls what Internet media types are sent to the client for
# given file extension(s). Sending the correct media type to the client
# is important so they know how to handle the content of the file.
# Extra types can either be added here or by using an AddType directive
# in your config files. For more information about Internet media types,
# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type
# registry is at <http://www.iana.org/assignments/media-types/>.
# MIME type Extensions
application/andrew-inset ez
application/atom+xml atom
application/java-archive jar
application/mac-binhex40 hqx
application/mac-compactpro cpt
application/mathml+xml mathml
application/msword doc
application/octet-stream bin dms lha lzh exe class so dll dmg
application/oda oda
application/ogg ogg
application/pdf pdf
application/postscript ai eps ps
application/rdf+xml rdf
application/smil smi smil
application/srgs gram
application/srgs+xml grxml
application/vnd.mif mif
application/vnd.mozilla.xul+xml xul
application/vnd.ms-excel xls
application/vnd.ms-powerpoint ppt
application/vnd.rn-realmedia rm
application/vnd.wap.wbxml wbxml
application/vnd.wap.wmlc wmlc
application/vnd.wap.wmlscriptc wmlsc
application/voicexml+xml vxml
application/x-bcpio bcpio
application/x-cdlink vcd
application/x-chess-pgn pgn
application/x-cpio cpio
application/x-csh csh
application/x-director dcr dir dxr
application/x-dvi dvi
application/x-futuresplash spl
application/x-gtar gtar
application/x-hdf hdf
application/x-java-jnlp-file jnlp
application/x-javascript js
application/x-koan skp skd skt skm
application/x-latex latex
application/x-netcdf nc cdf
application/x-sh sh
application/x-shar shar
application/x-shockwave-flash swf
application/x-stuffit sit
application/x-sv4cpio sv4cpio
application/x-sv4crc sv4crc
application/x-tar tar
application/x-tcl tcl
application/x-tex tex
application/x-texinfo texinfo texi
application/x-troff t tr roff
application/x-troff-man man
application/x-troff-me me
application/x-troff-ms ms
application/x-ustar ustar
application/x-wais-source src
application/xhtml+xml xhtml xht
application/xml xml xsl
application/xml-dtd dtd
application/xslt+xml xslt
application/zip zip
audio/basic au snd
audio/midi mid midi kar
audio/mpeg mpga mp2 mp3
audio/x-aiff aif aiff aifc
audio/x-mpegurl m3u
audio/x-pn-realaudio ram ra
audio/x-wav wav
chemical/x-pdb pdb
chemical/x-xyz xyz
image/bmp bmp
image/cgm cgm
image/gif gif
image/ief ief
image/jp2 jp2
image/jpeg jpeg jpg jpe
image/pict pict pic pct
image/png png
image/svg+xml svg
image/tiff tiff tif
image/vnd.djvu djvu djv
image/vnd.wap.wbmp wbmp
image/x-cmu-raster ras
image/x-icon ico
image/x-macpaint pntg pnt mac
image/x-portable-anymap pnm
image/x-portable-bitmap pbm
image/x-portable-graymap pgm
image/x-portable-pixmap ppm
image/x-quicktime qtif qti
image/x-rgb rgb
image/x-xbitmap xbm
image/x-xpixmap xpm
image/x-xwindowdump xwd
model/iges igs iges
model/mesh msh mesh silo
model/vrml wrl vrml
text/calendar ics ifb
text/css css
text/html html htm
text/plain asc txt
text/richtext rtx
text/rtf rtf
text/sgml sgml sgm
text/tab-separated-values tsv
text/vnd.wap.wml wml
text/vnd.wap.wmlscript wmls
text/x-setext etx
video/mp4 mp4
video/mpeg mpeg mpg mpe
video/quicktime qt mov
video/vnd.mpegurl mxu m4u
video/x-dv dv dif
video/x-msvideo avi
video/x-sgi-movie movie
x-conference/x-cooltalk ice

View File

@ -1,5 +1,3 @@
# This is a comment. I love comments.
# This file controls what Internet media types are sent to the client for
# given file extension(s). Sending the correct media type to the client
# is important so they know how to handle the content of the file.
@ -12,6 +10,7 @@
application/andrew-inset ez
application/atom+xml atom
application/java-archive jar
application/mac-binhex40 hqx
application/mac-compactpro cpt
application/mathml+xml mathml
@ -20,6 +19,7 @@ application/octet-stream bin dms lha lzh exe class so dll dmg
application/oda oda
application/ogg ogg
application/pdf pdf
application/pgp-encrypted pgp
application/postscript ai eps ps
application/rdf+xml rdf
application/smil smi smil
@ -66,7 +66,7 @@ application/x-troff-ms ms
application/x-ustar ustar
application/x-wais-source src
application/xhtml+xml xhtml xht
application/xml xml xsl
application/xml xml xsl pom
application/xml-dtd dtd
application/xslt+xml xslt
application/zip zip
@ -109,7 +109,7 @@ model/vrml wrl vrml
text/calendar ics ifb
text/css css
text/html html htm
text/plain asc txt
text/plain asc txt sha1 md5
text/richtext rtx
text/rtf rtf
text/sgml sgml sgm

View File

@ -1,13 +0,0 @@
#
# A simple property file defining some strings that will be returned and/or
# used by the Could.IT DAVServlet at different stages of processing
#
# Returned by DAVServlet in the "getServletInfo()" method
servlet.information = Could.IT WebDAV Servlet
# Added to the "Server" header every time a request is processed
servlet.signature = CouldIT-WebDAV
# Version used in build files and combined to information and signature
version = 0.5-dev

View File

@ -1,495 +0,0 @@
/*
* 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.maven.archiva.webdav;
import org.apache.commons.lang.NotImplementedException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
/**
* TestableHttpServletRequest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: TestableHttpServletRequest.java 6940 2007-10-16 01:02:02Z joakime $
*/
public class TestableHttpServletRequest
implements HttpServletRequest
{
public TestableHttpServletRequest()
{
setDefaults();
}
public void setDefaults()
{
authType = null;
scheme = "http";
protocol = "HTTP/1.1";
serverName = "localhost";
serverPort = 80;
remoteHost = "localhost";
}
private String authType;
private String characterEncoding;
private int contentLength;
private String contentType;
private String contextPath;
private Locale locale;
private String method;
private String pathInfo;
private String pathTranslated;
private String protocol;
private String queryString;
private String remoteAddr;
private String remoteHost;
private String remoteUser;
private String requestedSessionId;
private boolean requestedSessionIdFromCookie;
private boolean requestedSessionIdFromUrl;
private boolean requestedSessionIdValid;
private StringBuffer requestURL;
private String scheme;
private boolean secure;
private String serverName;
private int serverPort;
private String servletPath;
public Object getAttribute( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getAttribute(String)" ) );
}
public Enumeration getAttributeNames()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getAttributeNames()" ) );
}
public String getAuthType()
{
return authType;
}
public String getCharacterEncoding()
{
return characterEncoding;
}
public int getContentLength()
{
return contentLength;
}
public String getContentType()
{
return contentType;
}
public String getContextPath()
{
return contextPath;
}
public Cookie[] getCookies()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getCookies()" ) );
}
public long getDateHeader( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getDateHeader(String)" ) );
}
public String getHeader( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getHeader(String)" ) );
}
private Map headers = new HashMap();
public Enumeration getHeaderNames()
{
return new IterEnumeration( headers.keySet().iterator() );
}
public Enumeration getHeaders( String name )
{
throw new NotImplementedException( notImplemented( ".getHeaders(String)" ) );
}
public ServletInputStream getInputStream()
throws IOException
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getInputStream()" ) );
}
public int getIntHeader( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getIntHeader(String)" ) );
}
public Locale getLocale()
{
return locale;
}
public Enumeration getLocales()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getLocales()" ) );
}
public String getMethod()
{
return method;
}
public String getParameter( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getParameter(String)" ) );
}
public Map getParameterMap()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getParameterMap()" ) );
}
public Enumeration getParameterNames()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getParameterNames()" ) );
}
public String[] getParameterValues( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getParameterValues(String)" ) );
}
public String getPathInfo()
{
return pathInfo;
}
public String getPathTranslated()
{
return pathTranslated;
}
public String getProtocol()
{
return protocol;
}
public String getQueryString()
{
return queryString;
}
public BufferedReader getReader()
throws IOException
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getReader()" ) );
}
public String getRealPath( String path )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getRealPath(String)" ) );
}
public String getRemoteAddr()
{
return remoteAddr;
}
public String getRemoteHost()
{
return remoteHost;
}
public String getRemoteUser()
{
return remoteUser;
}
public RequestDispatcher getRequestDispatcher( String path )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getRequestDispatcher(String)" ) );
}
public String getRequestedSessionId()
{
return requestedSessionId;
}
public String getRequestURI()
{
return requestURL.toString();
}
public StringBuffer getRequestURL()
{
return requestURL;
}
public String getScheme()
{
return scheme;
}
public String getServerName()
{
return serverName;
}
public int getServerPort()
{
return serverPort;
}
public String getServletPath()
{
return servletPath;
}
public HttpSession getSession()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getSession()" ) );
}
public HttpSession getSession( boolean create )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getSession(boolean)" ) );
}
public Principal getUserPrincipal()
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".getUserPrincipal()" ) );
}
public boolean isRequestedSessionIdFromCookie()
{
return requestedSessionIdFromCookie;
}
public boolean isRequestedSessionIdFromUrl()
{
return requestedSessionIdFromUrl;
}
public boolean isRequestedSessionIdFromURL()
{
return requestedSessionIdFromUrl;
}
public boolean isRequestedSessionIdValid()
{
return requestedSessionIdValid;
}
public boolean isSecure()
{
return secure;
}
public boolean isUserInRole( String role )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".isUserInRole(String)" ) );
}
public void removeAttribute( String name )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".removeAttribute(String)" ) );
}
public void setAttribute( String name, Object o )
{
// TODO: Implement if needed.
throw new NotImplementedException( notImplemented( ".setAttribute(String, Object)" ) );
}
public void setCharacterEncoding( String encoding )
throws UnsupportedEncodingException
{
this.characterEncoding = encoding;
}
public void setContentLength( int contentLength )
{
this.contentLength = contentLength;
}
public void setContentType( String contentType )
{
this.contentType = contentType;
}
public void setContextPath( String contextPath )
{
this.contextPath = contextPath;
}
public void setMethod( String method )
{
this.method = method;
}
public void setPathInfo( String pathInfo )
{
this.pathInfo = pathInfo;
}
public void setProtocol( String protocol )
{
this.protocol = protocol;
}
public void setQueryString( String queryString )
{
this.queryString = queryString;
}
public void setScheme( String scheme )
{
this.scheme = scheme;
}
public void setSecure( boolean secure )
{
this.secure = secure;
}
public void setServerName( String serverName )
{
this.serverName = serverName;
}
public void setServerPort( int serverPort )
{
this.serverPort = serverPort;
}
public void setServletPath( String servletPath )
{
this.servletPath = servletPath;
}
public void setUrl( String urlString )
throws MalformedURLException
{
URL url = new URL( urlString );
this.queryString = url.getQuery();
this.scheme = url.getProtocol();
this.serverName = url.getHost();
this.serverPort = url.getPort();
String path = url.getPath();
if ( !path.startsWith( this.servletPath ) )
{
throw new MalformedURLException( "Unable to operate on request path [" + path
+ "] outside of servletPath [" + this.servletPath + "]." );
}
this.pathInfo = path.substring( this.servletPath.length() );
this.requestURL = new StringBuffer( this.pathInfo );
}
private String notImplemented( String msg )
{
return msg + " is not implemented in " + this.getClass().getName();
}
class IterEnumeration
implements Enumeration
{
private Iterator iter;
public IterEnumeration( Iterator it )
{
this.iter = it;
}
public boolean hasMoreElements()
{
return this.iter.hasNext();
}
public Object nextElement()
{
return this.iter.next();
}
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.maven.archiva.webdav.servlet.multiplexed;
import junit.framework.TestCase;
import org.apache.maven.archiva.webdav.TestableHttpServletRequest;
import org.apache.maven.archiva.webdav.util.WrappedRepositoryRequest;
import java.net.MalformedURLException;
/**
* MultiplexedDavServerRequestTest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: MultiplexedDavServerRequestTest.java 6940 2007-10-16 01:02:02Z joakime $
*/
public class MultiplexedDavServerRequestTest
extends TestCase
{
private void assertMultiURL( String expectedPrefix, String expectedLogicalResource, String url )
throws MalformedURLException
{
TestableHttpServletRequest testrequest = new TestableHttpServletRequest();
testrequest.setMethod( "GET" );
testrequest.setServletPath( "/repository" );
testrequest.setUrl( url );
WrappedRepositoryRequest wraprequest = new WrappedRepositoryRequest( testrequest );
MultiplexedDavServerRequest multirequest = new MultiplexedDavServerRequest( wraprequest );
assertEquals( expectedPrefix, multirequest.getPrefix() );
assertEquals( expectedLogicalResource, multirequest.getLogicalResource() );
}
public void testNormalUsage()
throws MalformedURLException
{
assertMultiURL( "corporate", "/", "http://localhost:9091/repository/corporate" );
assertMultiURL( "corporate", "/dom4j/dom4j/1.4", "http://localhost:9091/repository/corporate/dom4j/dom4j/1.4" );
}
public void testHacker()
throws MalformedURLException
{
assertMultiURL( "corporate", "/etc/passwd", "http://localhost:9091/repository/corporate//etc/passwd" );
// Since the double ".." puts the path outside of the /corporate/, it will return "/" as a hack fallback.
assertMultiURL( "corporate", "/", "http://localhost:9091/repository/corporate/dom4j/../../etc/passwd" );
assertMultiURL( "corporate", "/", "http://localhost:9091/repository/corporate/../.." );
}
}

View File

@ -1,38 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import org.apache.maven.archiva.webdav.test.AbstractBasicWebdavProviderTestCase;
/**
* SimpleDavServerComponentBasicTest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: SimpleDavServerComponentBasicTest.java 5408 2007-01-12 19:42:37Z joakime $
*/
public class SimpleDavServerComponentBasicTest
extends AbstractBasicWebdavProviderTestCase
{
public SimpleDavServerComponentBasicTest()
{
super();
setProviderHint( "simple" );
}
}

View File

@ -1,38 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import org.apache.maven.archiva.webdav.test.AbstractWebdavIndexHtmlTestCase;
/**
* SimpleDavServerComponentIndexHtmlTest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: SimpleDavServerComponentIndexHtmlTest.java 6000 2007-03-04 22:01:49Z joakime $
*/
public class SimpleDavServerComponentIndexHtmlTest
extends AbstractWebdavIndexHtmlTestCase
{
public SimpleDavServerComponentIndexHtmlTest()
{
super();
setProviderHint( "simple" );
}
}

View File

@ -1,38 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import org.apache.maven.archiva.webdav.test.AbstractMultiWebdavProviderTestCase;
/**
* SimpleDavServerComponentCrossTest
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: SimpleDavServerComponentMultiTest.java 5408 2007-01-12 19:42:37Z joakime $
*/
public class SimpleDavServerComponentMultiTest
extends AbstractMultiWebdavProviderTestCase
{
public SimpleDavServerComponentMultiTest()
{
super();
setProviderHint( "simple" );
}
}

View File

@ -1,50 +0,0 @@
/*
* 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.maven.archiva.webdav.simple;
import org.apache.maven.archiva.webdav.test.AbstractWebdavServer;
/**
* SimpleWebdavServer
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: SimpleWebdavServer.java 5379 2007-01-07 22:54:41Z joakime $
*/
public class SimpleWebdavServer
extends AbstractWebdavServer
{
public static void main( String[] args )
{
try
{
SimpleWebdavServer server = new SimpleWebdavServer();
server.startServer();
}
catch ( Exception e )
{
e.printStackTrace();
}
}
protected String getProviderHint()
{
return "simple";
}
}

View File

@ -1,255 +0,0 @@
/*
* 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.maven.archiva.webdav.test;
import org.apache.commons.httpclient.HttpURL;
import org.apache.maven.archiva.webdav.servlet.basic.BasicWebDavServlet;
import org.apache.webdav.lib.WebdavResource;
import org.codehaus.plexus.util.IOUtil;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* AbstractBasicWebdavProviderTestCase
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractBasicWebdavProviderTestCase.java 6000 2007-03-04 22:01:49Z joakime $
*/
public abstract class AbstractBasicWebdavProviderTestCase
extends AbstractWebdavProviderTestCase
{
private File serverRepoDir;
private WebdavResource davRepo;
/** The Jetty Server. */
private Server server;
protected void setUp()
throws Exception
{
super.setUp();
// Initialize server contents directory.
serverRepoDir = getTestDir( "sandbox" );
// Setup the Jetty Server.
System.setProperty( "DEBUG", "" );
System.setProperty( "org.mortbay.log.class", "org.slf4j.impl.SimpleLogger" );
server = new Server( PORT );
WebAppContext webAppConfig = new WebAppContext( server, getTestFile( "src/test/webapp" ).getCanonicalPath(), "/" );
ServletHandler servletHandler = webAppConfig.getServletHandler();
ServletHolder holder = servletHandler.addServletWithMapping( BasicWebDavServlet.class, CONTEXT + "/*" );
holder.setInitParameter( "dav.root", serverRepoDir.getAbsolutePath() );
server.start();
// Setup Client Side
HttpURL httpSandboxUrl = new HttpURL( "http://localhost:" + PORT + CONTEXT + "/" );
try
{
davRepo = new WebdavResource( httpSandboxUrl );
davRepo.setDebug( 8 );
davRepo.setPath( CONTEXT );
}
catch ( IOException e )
{
tearDown();
throw e;
}
}
protected void tearDown()
throws Exception
{
serverRepoDir = null;
if ( server != null )
{
try
{
server.stop();
}
catch ( Exception e )
{
/* ignore */
}
server = null;
}
if ( davRepo != null )
{
try
{
davRepo.close();
}
catch ( Exception e )
{
/* ignore */
}
davRepo = null;
}
super.tearDown();
}
// --------------------------------------------------------------------
// Actual Test Cases.
// --------------------------------------------------------------------
public void testPutGet()
throws Exception
{
// Quote: Rocky
String contents = "yo!\n";
assertDavTouchFile( davRepo, CONTEXT, "data.txt", contents );
InputStream inputStream = davRepo.getMethodData( CONTEXT + "/data.txt" );
assertEquals( contents, IOUtil.toString( inputStream ) );
}
public void testCollectionTasks()
throws Exception
{
// Create a few collections.
assertDavMkDir( davRepo, CONTEXT + "/bar" );
assertDavMkDir( davRepo, CONTEXT + "/bar/foo" );
// Remove a collection
davRepo.setPath( CONTEXT );
if ( !davRepo.deleteMethod( CONTEXT + "/bar/foo" ) )
{
fail( "Unable to remove <" + CONTEXT + "/bar/foo> on <" + davRepo.getHttpURL().toString() + "> due to <"
+ davRepo.getStatusMessage() + ">" );
}
assertDavDirNotExists( davRepo, CONTEXT + "/bar/foo" );
}
public void testResourceCopy()
throws Exception
{
// Lyrics: Cool and the Gang - Celebrate Good Times
String contents = "we're gonna have a good time tonite. lets celebrate. it's a celebration. "
+ "cel-e-brate good times, come on!";
// Create a few collections.
assertDavMkDir( davRepo, CONTEXT + "/bar" );
assertDavMkDir( davRepo, CONTEXT + "/foo" );
// Create a resource
assertDavTouchFile( davRepo, CONTEXT + "/bar", "data.txt", contents );
// Test for existance of resource
assertDavFileExists( davRepo, CONTEXT + "/bar", "data.txt" );
assertDavFileNotExists( davRepo, CONTEXT + "/foo", "data.txt" );
// Copy resource
String source = CONTEXT + "/bar/data.txt";
String dest = CONTEXT + "/foo/data.txt";
if ( !davRepo.copyMethod( source, dest ) )
{
fail( "Unable to copy <" + source + "> to <" + dest + "> on <" + davRepo.getHttpURL().toString()
+ "> due to <" + davRepo.getStatusMessage() + ">" );
}
// Test for existance of resource
assertDavFileExists( davRepo, CONTEXT + "/bar", "data.txt" );
assertDavFileExists( davRepo, CONTEXT + "/foo", "data.txt" );
}
public void testResourceMove()
throws Exception
{
// Lyrics: Men At Work - Who Can It Be Now
String contents = "Who can it be knocking at my door?\n" + "Make no sound, tip-toe across the floor.\n"
+ "If he hears, he'll knock all day,\n" + "I'll be trapped, and here I'll have to stay.\n"
+ "I've done no harm, I keep to myself;\n" + "There's nothing wrong with my state of mental health.\n"
+ "I like it here with my childhood friend;\n" + "Here they come, those feelings again!\n";
// Create a few collections.
assertDavMkDir( davRepo, CONTEXT + "/bar" );
assertDavMkDir( davRepo, CONTEXT + "/foo" );
// Create a resource
assertDavTouchFile( davRepo, CONTEXT + "/bar", "data.txt", contents );
// Test for existance of resource
assertDavFileExists( davRepo, CONTEXT + "/bar", "data.txt" );
assertDavFileNotExists( davRepo, CONTEXT + "/foo", "data.txt" );
// Copy resource
String source = CONTEXT + "/bar/data.txt";
String dest = CONTEXT + "/foo/data.txt";
if ( !davRepo.moveMethod( source, dest ) )
{
fail( "Unable to move <" + source + "> to <" + dest + "> on <" + davRepo.getHttpURL().toString()
+ "> due to <" + davRepo.getStatusMessage() + ">" );
}
// Test for existance of resource
assertDavFileNotExists( davRepo, CONTEXT + "/bar", "data.txt" );
assertDavFileExists( davRepo, CONTEXT + "/foo", "data.txt" );
}
public void testResourceDelete()
throws Exception
{
// Lyrics: Men At Work - Down Under
String contents = "Lying in a den in Bombay\n" + "With a slack jaw, and not much to say\n"
+ "I said to the man, \"Are you trying to tempt me\"\n" + "Because I come from the land of plenty?\n";
// Create a few collections.
assertDavMkDir( davRepo, CONTEXT + "/bar" );
// Create a resource
assertDavTouchFile( davRepo, CONTEXT + "/bar", "data.txt", contents );
// Move resource
davRepo.setPath( CONTEXT );
if ( !davRepo.deleteMethod( CONTEXT + "/bar/data.txt" ) )
{
fail( "Unable to remove <" + CONTEXT + "/bar/data.txt> on <" + davRepo.getHttpURL().toString()
+ "> due to <" + davRepo.getStatusMessage() + ">" );
}
// Test for existance via webdav interface.
assertDavFileNotExists( davRepo, CONTEXT + "/bar", "data.txt" );
}
}

View File

@ -1,203 +0,0 @@
/*
* 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.maven.archiva.webdav.test;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpURL;
import org.apache.webdav.lib.WebdavResource;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;
import java.io.File;
import java.io.IOException;
/**
* AbstractMultiWebdavProviderTestCase
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractMultiWebdavProviderTestCase.java 5997 2007-03-04 19:41:15Z joakime $
*/
public abstract class AbstractMultiWebdavProviderTestCase
extends AbstractWebdavProviderTestCase
{
File serverSandboxDir;
File serverSnapshotsDir;
/** The Jetty Server. */
private Server server;
private WebdavResource davSnapshots;
private WebdavResource davSandbox;
protected void setUp()
throws Exception
{
super.setUp();
// Initialize server contents directory.
serverSandboxDir = getTestDir( "sandbox" );
serverSnapshotsDir = getTestDir( "snapshots" );
// Setup the Jetty Server.
System.setProperty( "DEBUG", "" );
System.setProperty( "org.mortbay.log.class", "org.slf4j.impl.SimpleLogger" );
server = new Server( PORT );
WebAppContext webAppConfig = new WebAppContext( server, getTestFile( "src/test/webapp" ).getCanonicalPath(), "/" );
ServletHandler servletHandler = webAppConfig.getServletHandler();
ServletHolder holder = servletHandler.addServletWithMapping( TestMultiWebDavServlet.class, CONTEXT + "/*" );
holder.setInitParameter( "root.sandbox", serverSandboxDir.getAbsolutePath() );
holder.setInitParameter( "root.snapshots", serverSnapshotsDir.getAbsolutePath() );
System.out.println( "root.sandbox = " + serverSandboxDir.getAbsolutePath() );
System.out.println( "root.snapshots = " + serverSnapshotsDir.getAbsolutePath() );
server.start();
// Setup Client Side
HttpURL httpSandboxUrl = new HttpURL( "http://localhost:" + PORT + CONTEXT + "/sandbox/" );
HttpURL httpSnapshotsUrl = new HttpURL( "http://localhost:" + PORT + CONTEXT + "/snapshots/" );
try
{
davSandbox = new WebdavResource( httpSandboxUrl );
davSnapshots = new WebdavResource( httpSnapshotsUrl );
davSandbox.setDebug( 8 );
davSnapshots.setDebug( 8 );
davSandbox.setPath( CONTEXT + "/sandbox/" );
davSnapshots.setPath( CONTEXT + "/snapshots/" );
}
catch ( IOException e )
{
tearDown();
throw e;
}
}
protected void tearDown()
throws Exception
{
serverRootDir = null;
if ( server != null )
{
try
{
server.stop();
}
catch ( Exception e )
{
/* ignore */
}
server = null;
}
if ( davSandbox != null )
{
try
{
davSandbox.close();
}
catch ( Exception e )
{
/* ignore */
}
davSandbox = null;
}
if ( davSnapshots != null )
{
try
{
davSnapshots.close();
}
catch ( Exception e )
{
/* ignore */
}
davSnapshots = null;
}
super.tearDown();
}
public void testResourceMoveCrossWebdav()
throws Exception
{
// Create a few collections.
assertDavMkDir( davSandbox, CONTEXT + "/sandbox/bar" );
assertDavMkDir( davSnapshots, CONTEXT + "/snapshots/foo" );
// Create a resource
assertDavTouchFile( davSandbox, CONTEXT + "/sandbox/bar", "data.txt", "yo!" );
// Move resource URL to URL (Across the WebDav Servlets)
davSandbox.setPath( CONTEXT + "/sandbox/bar" );
String source = CONTEXT + "/sandbox/bar/data.txt";
String dest = "http://localhost:" + PORT + CONTEXT + "/snapshots/foo/data.txt";
if ( !davSandbox.moveMethod( source, dest ) )
{
// TODO: remove when fully implemented.
if ( davSandbox.getStatusCode() == HttpStatus.SC_NOT_IMPLEMENTED )
{
// return quietly, as the server reported no support for this method.
return;
}
fail( "Unable to move <" + source + "> to <" + dest + "> on <" + davSandbox.getHttpURL().toString()
+ "> due to <" + davSandbox.getStatusMessage() + ">" );
}
assertDavFileNotExists( davSandbox, CONTEXT + "/sandbox/bar", "data.txt" );
assertDavFileExists( davSnapshots, CONTEXT + "/snapshots/foo", "data.txt" );
}
public void testResourceDoesNotExist()
throws Exception
{
// Create a few collections.
assertDavMkDir( davSandbox, CONTEXT + "/sandbox/bar" );
assertDavMkDir( davSnapshots, CONTEXT + "/snapshots/foo" );
// Create a resource
assertDavTouchFile( davSandbox, CONTEXT + "/sandbox/bar", "data.txt", "yo!" );
// Get bad resources URLs
String urlPrefix = "http://localhost:" + PORT + CONTEXT;
assertGet404( urlPrefix + "/sandbox/a/resource/that/does/not/exist.html" );
assertGet404( urlPrefix + "/" );
assertGet404( urlPrefix + "/snapshots/foo/index.html" );
assertGet404( urlPrefix + "/sandbox/bar.html" );
assertGet404( urlPrefix + "/nonexistant/index.html" );
}
}

View File

@ -1,148 +0,0 @@
/*
* 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.maven.archiva.webdav.test;
import org.apache.commons.httpclient.HttpURL;
import org.apache.maven.archiva.webdav.servlet.basic.BasicWebDavServlet;
import org.apache.webdav.lib.WebdavResource;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;
import java.io.File;
import java.io.IOException;
public abstract class AbstractWebdavIndexHtmlTestCase
extends AbstractWebdavProviderTestCase
{
private File serverRepoDir;
private WebdavResource davRepo;
/** The Jetty Server. */
private Server server;
protected void setUp()
throws Exception
{
super.setUp();
// Initialize server contents directory.
serverRepoDir = getTestDir( "sandbox" );
// Setup the Jetty Server.
System.setProperty( "DEBUG", "" );
System.setProperty( "org.mortbay.log.class", "org.slf4j.impl.SimpleLogger" );
server = new Server( PORT );
WebAppContext webAppConfig = new WebAppContext( server, getTestFile( "src/test/webapp" ).getCanonicalPath(), "/" );
ServletHandler servletHandler = webAppConfig.getServletHandler();
ServletHolder holder = servletHandler.addServletWithMapping( BasicWebDavServlet.class, CONTEXT + "/*" );
holder.setInitParameter( BasicWebDavServlet.INIT_ROOT_DIRECTORY, serverRepoDir.getAbsolutePath() );
holder.setInitParameter( BasicWebDavServlet.INIT_USE_INDEX_HTML, "true" );
server.start();
// Setup Client Side
HttpURL httpSandboxUrl = new HttpURL( "http://localhost:" + PORT + CONTEXT + "/" );
try
{
davRepo = new WebdavResource( httpSandboxUrl );
davRepo.setDebug( 8 );
davRepo.setPath( CONTEXT );
}
catch ( IOException e )
{
tearDown();
throw e;
}
}
protected void tearDown()
throws Exception
{
serverRepoDir = null;
if ( server != null )
{
try
{
server.stop();
}
catch ( Exception e )
{
/* ignore */
}
server = null;
}
if ( davRepo != null )
{
try
{
davRepo.close();
}
catch ( Exception e )
{
/* ignore */
}
davRepo = null;
}
super.tearDown();
}
public void testCollectionIndexHtml()
throws Exception
{
// Lyrics: Colin Hay - Overkill
String contents = "I cant get to sleep\n" + "I think about the implications\n" + "Of diving in too deep\n"
+ "And possibly the complications\n" + "Especially at night\n" + "I worry over situations\n"
+ "I know will be alright\n" + "Perahaps its just my imagination\n" + "Day after day it reappears\n"
+ "Night after night my heartbeat, shows the fear\n" + "Ghosts appear and fade away";
// Create a few collections.
assertDavMkDir( davRepo, CONTEXT + "/bar" );
assertDavMkDir( davRepo, CONTEXT + "/foo" );
// Create a resource
assertDavTouchFile( davRepo, CONTEXT + "/bar", "index.html", contents );
// Test for existance of resource
assertDavFileExists( davRepo, CONTEXT + "/bar", "index.html" );
assertDavFileNotExists( davRepo, CONTEXT + "/foo", "index.html" );
// Copy resource
String actual = davRepo.getMethodDataAsString( CONTEXT + "/bar/" );
assertEquals( contents, actual );
}
}

View File

@ -1,401 +0,0 @@
/*
* 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.maven.archiva.webdav.test;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.FileUtils;
import org.apache.maven.archiva.webdav.DavServerManager;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.WebdavResources;
import org.codehaus.plexus.spring.PlexusInSpringTestCase;
import java.io.File;
import java.io.IOException;
/**
* AbstractWebdavProviderTestCase
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractWebdavProviderTestCase.java 5997 2007-03-04 19:41:15Z joakime $
*/
public abstract class AbstractWebdavProviderTestCase
extends PlexusInSpringTestCase
{
public static final int PORT = 4321;
public static final String CONTEXT = "/repos";
protected File serverRootDir = null;
private DavServerManager manager;
private String providerHint = "simple";
public DavServerManager getManager()
{
return manager;
}
public String getProviderHint()
{
return providerHint;
}
public void setManager( DavServerManager manager )
{
this.manager = manager;
}
public void setProviderHint( String providerHint )
{
this.providerHint = providerHint;
}
protected void setUp()
throws Exception
{
super.setUp();
try
{
manager = (DavServerManager) lookup( DavServerManager.ROLE, getProviderHint() );
serverRootDir = getRootDir();
}
catch ( Exception e )
{
tearDown();
throw e;
}
}
protected void tearDown()
throws Exception
{
serverRootDir = null;
super.tearDown();
}
protected void dumpCollection( WebdavResource webdavResource, String path )
throws Exception
{
webdavResource.setPath( path );
WebdavResource resources[] = webdavResource.listWebdavResources();
System.out.println( "Dump Collection [" + path + "]: " + resources.length + " hits." );
dumpCollectionRecursive( "", webdavResource, path );
}
protected void dumpCollectionRecursive( String indent, WebdavResource webdavResource, String path )
throws Exception
{
if ( indent.length() > 12 )
{
return;
}
WebdavResource resources[] = webdavResource.listWebdavResources();
for ( int i = 0; i < resources.length; i++ )
{
System.out.println( indent + "WebDavResource[" + path + "|" + i + "]: "
+ ( resources[i].isCollection() ? "(collection) " : "" ) + resources[i].getName() );
if ( resources[i].isCollection() )
{
dumpCollectionRecursive( indent + " ", resources[i], path + "/" + resources[i].getName() );
}
}
}
// --------------------------------------------------------------------
// Actual Test Cases.
// --------------------------------------------------------------------
public void assertNotExists( File basedir, String relativePath )
{
assertNotExists( new File( basedir, relativePath ) );
}
public void assertNotExists( File file )
{
if ( file.exists() )
{
fail( "Unexpected path <" + file.getAbsolutePath() + "> should not exist." );
}
}
public void assertExists( File basedir, String relativePath )
{
assertExists( new File( basedir, relativePath ) );
}
public void assertExists( File file )
{
if ( !file.exists() )
{
fail( "Expected path <" + file.getAbsolutePath() + "> does not exist." );
}
}
private void resetDirectory( File dir )
{
try
{
FileUtils.deleteDirectory( dir );
}
catch ( IOException e )
{
fail( "Unable to delete test directory [" + dir.getAbsolutePath() + "]." );
}
if ( dir.exists() )
{
fail( "Unable to execute test, test directory [" + dir.getAbsolutePath()
+ "] exists, and cannot be deleted by the test case." );
}
if ( !dir.mkdirs() )
{
fail( "Unable to execute test, test directory [" + dir.getAbsolutePath() + "] cannot be created." );
}
}
private File getRootDir()
{
if ( this.serverRootDir == null )
{
String clazz = this.getClass().getName();
clazz = clazz.substring( clazz.lastIndexOf( "." ) + 1 );
serverRootDir = new File( "target/test-contents-" + clazz + "/" + getName() );
resetDirectory( serverRootDir );
}
return serverRootDir;
}
protected File getTestDir( String subdir )
{
File testDir = new File( getRootDir(), subdir );
resetDirectory( testDir );
return testDir;
}
public boolean isHttpStatusOk( WebdavResource webdavResource )
{
int statusCode = webdavResource.getStatusCode();
if ( statusCode == HttpStatus.SC_MULTI_STATUS )
{
// TODO: find out multi-status values.
}
return ( statusCode >= 200 ) && ( statusCode < 300 );
}
public void assertDavMkDir( WebdavResource webdavResource, String collectionName )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
if ( !webdavResource.mkcolMethod( collectionName ) )
{
fail( "Unable to create collection/dir <" + collectionName + "> against <" + httpurl + "> due to <"
+ webdavResource.getStatusMessage() + ">" );
}
assertDavDirExists( webdavResource, collectionName );
}
public void assertDavFileExists( WebdavResource webdavResource, String path, String filename )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
if ( !webdavResource.headMethod( path + "/" + filename ) )
{
fail( "Unable to verify that file/contents <" + path + "/" + filename + "> exists against <" + httpurl
+ "> due to <" + webdavResource.getStatusMessage() + ">" );
}
String oldPath = webdavResource.getPath();
try
{
webdavResource.setPath( path );
WebdavResources resources = webdavResource.getChildResources();
WebdavResource testResource = resources.getResource( filename );
if ( testResource == null )
{
fail( "The file/contents <" + path + "/" + filename + "> does not exist in <" + httpurl + ">" );
}
if ( testResource.isCollection() )
{
fail( "The file/contents <" + path + "/" + filename
+ "> is incorrectly being reported as a collection." );
}
}
finally
{
webdavResource.setPath( oldPath );
}
}
public void assertDavFileNotExists( WebdavResource webdavResource, String path, String filename )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
if ( webdavResource.headMethod( path + "/" + filename ) )
{
fail( "Encountered unexpected file/contents <" + path + "/" + filename + "> at <" + httpurl + ">" );
}
String oldPath = webdavResource.getPath();
try
{
webdavResource.setPath( path );
WebdavResources resources = webdavResource.getChildResources();
WebdavResource testResource = resources.getResource( filename );
if ( testResource == null )
{
// Nothing found. we're done.
return;
}
if ( !testResource.isCollection() )
{
fail( "Encountered unexpected file/contents <" + path + "/" + filename + "> at <" + httpurl + ">" );
}
}
finally
{
webdavResource.setPath( oldPath );
}
}
public void assertDavDirExists( WebdavResource webdavResource, String path )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
String oldPath = webdavResource.getPath();
try
{
webdavResource.setPath( path );
if ( !webdavResource.isCollection() )
{
if ( !isHttpStatusOk( webdavResource ) )
{
fail( "Unable to verify that path <" + path + "> is really a collection against <" + httpurl
+ "> due to <" + webdavResource.getStatusMessage() + ">" );
}
}
}
finally
{
webdavResource.setPath( oldPath );
}
}
public void assertDavDirNotExists( WebdavResource webdavResource, String path )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
String oldPath = webdavResource.getPath();
try
{
webdavResource.setPath( path );
if ( webdavResource.isCollection() )
{
fail( "Encountered unexpected collection <" + path + "> at <" + httpurl + ">" );
}
}
catch ( HttpException e )
{
if ( e.getReasonCode() == HttpStatus.SC_NOT_FOUND )
{
// Expected path.
return;
}
fail( "Unable to set path due to HttpException: " + e.getReasonCode() + ":" + e.getReason() );
}
finally
{
webdavResource.setPath( oldPath );
}
}
public void assertDavTouchFile( WebdavResource webdavResource, String path, String filename, String contents )
throws Exception
{
String httpurl = webdavResource.getHttpURL().toString();
webdavResource.setPath( path );
if ( !webdavResource.putMethod( path + "/" + filename, contents ) )
{
fail( "Unable to create file/contents <" + path + "/" + filename + "> against <" + httpurl + "> due to <"
+ webdavResource.getStatusMessage() + ">" );
}
assertDavFileExists( webdavResource, path, filename );
}
protected void assertGet404( String url )
throws IOException
{
HttpClient client = new HttpClient();
GetMethod method = new GetMethod( url );
try
{
client.executeMethod( method );
if ( method.getStatusCode() == 404 )
{
// Expected path.
return;
}
fail( "Request for resource " + url + " should have resulted in an HTTP 404 (Not Found) response, "
+ "instead got code " + method.getStatusCode() + " <" + method.getStatusText() + ">." );
}
catch ( HttpException e )
{
System.err.println( "HTTP Response: " + e.getReasonCode() + " " + e.getReason() );
throw e;
}
}
}

View File

@ -1,163 +0,0 @@
/*
* 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.maven.archiva.webdav.test;
import org.apache.maven.archiva.webdav.DavServerManager;
import org.apache.maven.archiva.webdav.servlet.basic.BasicWebDavServlet;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.spring.PlexusContainerAdapter;
import org.codehaus.plexus.util.FileUtils;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* AbstractWebdavServer - Baseline server for starting up a BasicWebDavServlet to allow experimentation with.
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id: AbstractWebdavServer.java 5407 2007-01-12 19:41:09Z joakime $
*/
public abstract class AbstractWebdavServer
{
public static final int PORT = 14541;
protected PlexusContainer container;
protected String basedir;
/** the jetty server */
protected Server server;
private DavServerManager manager;
public String getBasedir()
{
if ( basedir != null )
{
return basedir;
}
basedir = System.getProperty( "basedir" );
if ( basedir == null )
{
basedir = new File( "" ).getAbsolutePath();
}
return basedir;
}
public File getTestFile( String path )
{
return new File( getBasedir(), path );
}
protected abstract String getProviderHint();
public void startServer()
throws Exception
{
container = createContainerInstance();
// ----------------------------------------------------------------------------
// Create the DavServerManager
// ----------------------------------------------------------------------------
manager = (DavServerManager) container.lookup( DavServerManager.ROLE, getProviderHint() );
// ----------------------------------------------------------------------------
// Create the jetty server
// ----------------------------------------------------------------------------
System.setProperty( "DEBUG", "" );
System.setProperty( "org.mortbay.log.class", "org.slf4j.impl.SimpleLogger" );
server = new Server( PORT );
Context root = new Context( server, "/", Context.SESSIONS );
ServletHandler servletHandler = root.getServletHandler();
root.setContextPath( "/" );
root.setAttribute( PlexusConstants.PLEXUS_KEY, container );
// ----------------------------------------------------------------------------
// Configure the webdav servlet
// ----------------------------------------------------------------------------
ServletHolder holder = servletHandler.addServletWithMapping( BasicWebDavServlet.class, "/projects/*" );
// Initialize server contents directory.
File serverContentsDir = new File( "target/test-server/" );
FileUtils.deleteDirectory( serverContentsDir );
if ( serverContentsDir.exists() )
{
throw new IllegalStateException( "Unable to execute test, server contents test directory ["
+ serverContentsDir.getAbsolutePath() + "] exists, and cannot be deleted by the test case." );
}
if ( !serverContentsDir.mkdirs() )
{
throw new IllegalStateException( "Unable to execute test, server contents test directory ["
+ serverContentsDir.getAbsolutePath() + "] cannot be created." );
}
holder.setInitParameter( "dav.root", serverContentsDir.getAbsolutePath() );
// ----------------------------------------------------------------------------
// Start the jetty server
// ----------------------------------------------------------------------------
server.start();
}
protected PlexusContainer createContainerInstance()
throws PlexusContainerException
{
return new PlexusContainerAdapter();
}
public void stopServer()
{
if ( server != null )
{
try
{
server.stop();
}
catch ( Exception e )
{
e.printStackTrace();
}
}
if ( container != null )
{
container.dispose();
}
}
}

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