Submitted by: James William Dumay git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@649352 13f79535-47bb-0310-9956-ffa450edef68tags/archiva-r676265
@@ -255,7 +255,7 @@ | |||
</build> | |||
<pluginRepositories> | |||
<pluginRepository> | |||
<id>codehaus.org</id> | |||
<id>codehaus.snapshots</id> | |||
<url>http://snapshots.repository.codehaus.org/</url> | |||
</pluginRepository> | |||
</pluginRepositories> |
@@ -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; |
@@ -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> |
@@ -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() |
@@ -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() |
@@ -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() |
@@ -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 ) |
@@ -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 <relocation> 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 ); | |||
} | |||
} |
@@ -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,157 +157,91 @@ public class RepositoryServlet | |||
continue; | |||
} | |||
} | |||
DavServerComponent server = createServer( repo.getId(), repoDir, servletConfig ); | |||
server.setUseIndexHtml( true ); | |||
} | |||
resourceFactory = (DavResourceFactory)wac.getBean(PlexusToSpringUtils.buildSpringId(ArchivaDavResourceFactory.class)); | |||
locatorFactory = new ArchivaDavLocatorFactory(); | |||
sessionProvider = new ArchivaDavSessionProvider(wac); | |||
} | |||
@Override | |||
protected void service( HttpServletRequest httpRequest, HttpServletResponse httpResponse ) | |||
throws ServletException, IOException | |||
public void configurationEvent( ConfigurationEvent event ) | |||
{ | |||
// Wrap the incoming request to adjust paths and whatnot. | |||
super.service( new PolicingServletRequest( httpRequest ), httpResponse ); | |||
if( event.getType() == ConfigurationEvent.SAVED ) | |||
{ | |||
initRepositories(); | |||
} | |||
} | |||
public synchronized ManagedRepositoryConfiguration getRepository( String prefix ) | |||
private void initRepositories() | |||
{ | |||
if ( repositoryMap.isEmpty() ) | |||
synchronized ( repositoryMap ) | |||
{ | |||
repositoryMap.clear(); | |||
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() ); | |||
} | |||
return repositoryMap.get( prefix ); | |||
} | |||
private String getRepositoryName( DavServerRequest request ) | |||
{ | |||
ManagedRepositoryConfiguration repoConfig = getRepository( request.getPrefix() ); | |||
if ( repoConfig == null ) | |||
synchronized ( reloadLock ) | |||
{ | |||
return "Unknown"; | |||
initServers( getServletConfig() ); | |||
} | |||
return repoConfig.getName(); | |||
} | |||
public boolean isAuthenticated( DavServerRequest davRequest, HttpServletResponse response ) | |||
throws ServletException, IOException | |||
public synchronized ManagedRepositoryConfiguration getRepository( String prefix ) | |||
{ | |||
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 ) | |||
if ( repositoryMap.isEmpty() ) | |||
{ | |||
httpAuth.challenge( request, response, "Repository " + getRepositoryName( davRequest ), | |||
new AuthenticationException( "You must change your password." ) ); | |||
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() ); | |||
} | |||
return true; | |||
return repositoryMap.get( prefix ); | |||
} | |||
public boolean isAuthorized( DavServerRequest davRequest, HttpServletResponse response ) | |||
throws ServletException, IOException | |||
ArchivaConfiguration getConfiguration() | |||
{ | |||
// 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; | |||
} | |||
return configuration; | |||
} | |||
AuthorizationResult authzResult = | |||
securitySystem.authorize( securitySession, permission, davRequest.getPrefix() ); | |||
protected boolean isPreconditionValid(final WebdavRequest request, final DavResource davResource) | |||
{ | |||
return true; | |||
} | |||
if ( !authzResult.isAuthorized() ) | |||
{ | |||
if ( authzResult.getException() != null ) | |||
{ | |||
log( "Authorization Denied [ip=" + request.getRemoteAddr() + ",isWriteRequest=" + isWriteRequest + | |||
",permission=" + permission + ",repo=" + davRequest.getPrefix() + "] : " + | |||
authzResult.getException().getMessage() ); | |||
} | |||
public DavSessionProvider getDavSessionProvider() | |||
{ | |||
return sessionProvider; | |||
} | |||
// 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." ); | |||
} | |||
public void setDavSessionProvider(final DavSessionProvider davSessionProvider) | |||
{ | |||
this.sessionProvider = davSessionProvider; | |||
} | |||
return true; | |||
public DavLocatorFactory getLocatorFactory() | |||
{ | |||
return locatorFactory; | |||
} | |||
public void configurationEvent( ConfigurationEvent event ) | |||
public void setLocatorFactory(final DavLocatorFactory davLocatorFactory) | |||
{ | |||
if( event.getType() == ConfigurationEvent.SAVED ) | |||
{ | |||
initRepositories(); | |||
} | |||
locatorFactory = davLocatorFactory; | |||
} | |||
private void initRepositories() | |||
public DavResourceFactory getResourceFactory() | |||
{ | |||
synchronized ( repositoryMap ) | |||
{ | |||
repositoryMap.clear(); | |||
repositoryMap.putAll( configuration.getConfiguration().getManagedRepositoriesAsMap() ); | |||
} | |||
return resourceFactory; | |||
} | |||
synchronized ( davManager ) | |||
{ | |||
// Clear out the old servers. | |||
davManager.removeAllServers(); | |||
public void setResourceFactory(final DavResourceFactory davResourceFactory) | |||
{ | |||
resourceFactory = davResourceFactory; | |||
} | |||
// Create new servers. | |||
try | |||
{ | |||
initServers( getServletConfig() ); | |||
} | |||
catch ( DavServerException e ) | |||
{ | |||
log( "Unable to init servers: " + e.getMessage(), e ); | |||
} | |||
} | |||
public String getAuthenticateHeaderValue() | |||
{ | |||
throw new UnsupportedOperationException(""); | |||
} | |||
ArchivaConfiguration getConfiguration() | |||
public String getAuthenticateHeaderValue(String repository) | |||
{ | |||
return configuration; | |||
return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\""; | |||
} | |||
} |
@@ -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 |
@@ -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")); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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,14 +73,17 @@ | |||
<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> | |||
@@ -52,36 +91,22 @@ | |||
<version>2.5.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<groupId>org.apache.jackrabbit</groupId> | |||
<artifactId>jackrabbit-webdav</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> | |||
<groupId>org.apache.maven</groupId> | |||
<artifactId>maven-model</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
</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 --> |
@@ -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 | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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(); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 | |||
"<code>£ 100</code>", or in details: | |||
</p> | |||
<ul> | |||
<li>a latin capital letter "A" 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 | |||
"<code>£ 100</code>", 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> |
@@ -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 "<code>.</code>" this method | |||
* will behave exactly like {@link #refresh()} and <i>this instance</i> | |||
* will be returned.</p> | |||
* | |||
* <p>If the specified child is "<code>..</code>" 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)); | |||
} | |||
} |
@@ -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> |
@@ -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 "user info" 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; | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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> |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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. | |||
} | |||
} | |||
} |
@@ -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 "/" | |||
* 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 "/" | |||
* 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; | |||
} | |||
} |
@@ -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 | |||
* "<code>rootPath</code>" 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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(">"); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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> |
@@ -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> | |||
<servlet> | |||
<servlet-name>dav</servlet-name> | |||
<servlet-class>it.could.webdav.DAVServlet</servlet-class> | |||
<init-param> | |||
<param-name>rootPath</param-name> | |||
<param-value>dav</param-value> | |||
</init-param> | |||
<init-param> | |||
<param-name>xmlOnly</param-name> | |||
<param-value>false</param-value> | |||
</init-param> | |||
<init-param> | |||
<param-name>debugEnabled</param-name> | |||
<param-value>false</param-value> | |||
</init-param> | |||
<load-on-startup>1</load-on-startup> | |||
</servlet> | |||
... | |||
<servlet-mapping> | |||
<servlet-name>dav</servlet-name> | |||
<url-pattern>/dav/*</url-pattern> | |||
</servlet-mapping> | |||
</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="<code>false</code>"]</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="<code>false</code>"]</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> |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 "<code>repository</code>" indicating the name of | |||
* the {@link DAVServlet} in the "<code>web.xml</code>" deployment | |||
* descriptor whose repository should be replicated.</p> | |||
* | |||
* <p>The second required parameter "<code>replicas</code>" | |||
* 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(); | |||
} | |||
} | |||
} |
@@ -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> |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 <relocation> 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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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."); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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 ); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
public String getLogicalResource(); | |||
private String path; | |||
public void setLogicalResource( String logicalResource ); | |||
public String getPath() | |||
{ | |||
return path; | |||
} | |||
public WrappedRepositoryRequest getRequest(); | |||
public void setPath(String path) | |||
{ | |||
this.path = path; | |||
} | |||
} |
@@ -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 DavServerException( String message ) | |||
{ | |||
super( message ); | |||
} | |||
public DavServerException( Throwable cause ) | |||
public UnauthorizedDavException(String repositoryName, String message) | |||
{ | |||
super( cause ); | |||
super(HttpServletResponse.SC_UNAUTHORIZED, message); | |||
this.repositoryName = repositoryName; | |||
} | |||
public DavServerException( String message, Throwable cause ) | |||
public String getRepositoryName() | |||
{ | |||
super( message, cause ); | |||
return repositoryName; | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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>"); | |||
} | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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, '/'); | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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> |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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/../.." ); | |||
} | |||
} |
@@ -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" ); | |||
} | |||
} |
@@ -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" ); | |||
} | |||
} |
@@ -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" ); | |||
} | |||
} |
@@ -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"; | |||
} | |||
} |
@@ -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" ); | |||
} | |||
} |
@@ -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" ); | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |