Index: src/java/org/apache/hadoop/fs/webdav/WebdavServer.java =================================================================== --- src/java/org/apache/hadoop/fs/webdav/WebdavServer.java (revision 0) +++ src/java/org/apache/hadoop/fs/webdav/WebdavServer.java (revision 0) @@ -0,0 +1,92 @@ +/** + * 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.hadoop.fs.webdav; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.mortbay.http.SocketListener; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.WebApplicationContext; + +public class WebdavServer { + + private static final Log LOG = LogFactory.getLog(WebdavServer.class); + + private Server webServer; + + /** @param name The name of the server + * @param port The port to use on the server + * @param findPort whether the server should start at the given port and + * increment by 1 until it finds a free port. + */ + public WebdavServer(String bindAddress, int port) throws Exception { + LOG.info("Initializing webdav server"); + webServer = new Server(); + SocketListener listener = new SocketListener(); + listener.setPort(port); + listener.setHost(bindAddress); + webServer.addListener(listener); + WebApplicationContext webAppContext = new WebApplicationContext(); + webAppContext.setContextPath("/"); + webAppContext.addServlet("/*", WebdavServlet.class.getName()); + webServer.addContext(webAppContext); + } + + public void start() throws Exception { + webServer.start(); + } + + public static void main(String[] args) throws Exception { + String usage = "WebdavServer"; + String header = "Run a webdav interface to a hadoop filesystem."; + Options options = new Options(); + options.addOption("l", "listen", true, "address to listen to"); + options.addOption("p", "port", true, "port to bind to"); + options.addOption("n", "fs", true, "value for fs.default.name (eg. namenode:port)"); + options.addOption("h", "help", false, "print usage information"); + CommandLineParser parser = new GnuParser(); + CommandLine cmd = parser.parse(options, args); + if (cmd.hasOption("h")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(usage, header, options, ""); + return; + } + int port = Integer.parseInt(cmd.getOptionValue("port", "20015")); + + // we use this cheesy way of passing the configuration to the WebdavServlet because + // jetty-5 doesn't have a way to send it in the WebdavServlet constructor + Configuration config = new Configuration(); + String fsDefaultName = cmd.getOptionValue("fs", null); + if (fsDefaultName != null) { + config.set("fs.default.name", fsDefaultName); + } + WebdavServlet.setConf(config); + + WebdavServer server = new WebdavServer(cmd.getOptionValue("l", "0.0.0.0"), port); + LOG.info("Starting webdav server"); + server.start(); + } + +} Index: src/java/org/apache/hadoop/fs/webdav/FakeDavSessionProvider.java =================================================================== --- src/java/org/apache/hadoop/fs/webdav/FakeDavSessionProvider.java (revision 0) +++ src/java/org/apache/hadoop/fs/webdav/FakeDavSessionProvider.java (revision 0) @@ -0,0 +1,34 @@ +/** + * 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.hadoop.fs.webdav; + +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.DavSessionProvider; +import org.apache.jackrabbit.webdav.WebdavRequest; + +public class FakeDavSessionProvider implements DavSessionProvider { + + public boolean attachSession(WebdavRequest request) throws DavException { + return true; + } + + public void releaseSession(WebdavRequest request) { + } + +} Index: src/java/org/apache/hadoop/fs/webdav/FSDavResourceFactory.java =================================================================== --- src/java/org/apache/hadoop/fs/webdav/FSDavResourceFactory.java (revision 0) +++ src/java/org/apache/hadoop/fs/webdav/FSDavResourceFactory.java (revision 0) @@ -0,0 +1,73 @@ +/** + * 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.hadoop.fs.webdav; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.DavMethods; +import org.apache.jackrabbit.webdav.DavResource; +import org.apache.jackrabbit.webdav.DavResourceFactory; +import org.apache.jackrabbit.webdav.DavResourceLocator; +import org.apache.jackrabbit.webdav.DavServletRequest; +import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.DavSession; +import org.apache.jackrabbit.webdav.simple.ResourceConfig; + +public class FSDavResourceFactory implements DavResourceFactory { + + private static final Log LOG = LogFactory.getLog(FSDavResourceFactory.class); + + private final ResourceConfig resourceConfig; + private final Configuration conf; + + public FSDavResourceFactory(ResourceConfig resourceConfig, Configuration conf) { + this.resourceConfig = resourceConfig; + this.conf = conf; + } + + public DavResource createResource(DavResourceLocator locator, + DavSession session) throws DavException { + + LOG.debug("createResource(DavResourceLocator, DavSession)"); + try { + return new FSDavResource(this, locator, session, resourceConfig, conf); + } + catch (IOException ex) { + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } + } + + public DavResource createResource(DavResourceLocator locator, + DavServletRequest request, DavServletResponse response) + throws DavException { + + LOG.debug("createResource(DavResourceLocator, DavServletRequest, DavServletResponse)"); + try { + return new FSDavResource(this, locator, request.getDavSession(), resourceConfig, conf + ,DavMethods.isCreateCollectionRequest(request) ); + } + catch (IOException ex) { + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } + } +} Index: src/java/org/apache/hadoop/fs/webdav/FSDavResource.java =================================================================== --- src/java/org/apache/hadoop/fs/webdav/FSDavResource.java (revision 0) +++ src/java/org/apache/hadoop/fs/webdav/FSDavResource.java (revision 0) @@ -0,0 +1,427 @@ +/** + * 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.hadoop.fs.webdav; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.util.StringUtils; +import org.apache.jackrabbit.webdav.DavCompliance; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.DavResource; +import org.apache.jackrabbit.webdav.DavResourceFactory; +import org.apache.jackrabbit.webdav.DavResourceIterator; +import org.apache.jackrabbit.webdav.DavResourceIteratorImpl; +import org.apache.jackrabbit.webdav.DavResourceLocator; +import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.DavSession; +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.io.InputContext; +import org.apache.jackrabbit.webdav.io.OutputContext; +import org.apache.jackrabbit.webdav.lock.ActiveLock; +import org.apache.jackrabbit.webdav.lock.LockInfo; +import org.apache.jackrabbit.webdav.lock.LockManager; +import org.apache.jackrabbit.webdav.lock.Scope; +import org.apache.jackrabbit.webdav.lock.Type; +import org.apache.jackrabbit.webdav.property.DavProperty; +import org.apache.jackrabbit.webdav.property.DavPropertyName; +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; +import org.apache.jackrabbit.webdav.property.DavPropertySet; +import org.apache.jackrabbit.webdav.property.DefaultDavProperty; +import org.apache.jackrabbit.webdav.property.ResourceType; +import org.apache.jackrabbit.webdav.simple.ResourceConfig; + +public class FSDavResource implements DavResource { + + private static final Log LOG = LogFactory.getLog(FSDavResource.class); + + private static final String COMPLIANCE_CLASS = + DavCompliance.concatComplianceClasses(new String[] {DavCompliance._1_}); + + //We support compliance level 1, and the listed methods. PROPFIND, PROPPATCH + //are not supported for now. + private static final String SUPPORTED_METHODS + = "OPTIONS, GET, HEAD, POST, TRACE, MKCOL, COPY, PUT, DELETE, MOVE"; + + private FSDavResourceFactory factory; + private final DavResourceLocator locator; + private LockManager lockManager; + private DavSession session; + + private DavPropertySet properties = new DavPropertySet(); + + //hadoop objects + private final Configuration conf; + private final FileSystem fs; + private final Path path; //the path object that this resource represents + private boolean inited = false; + + /** + * This only indicates that the DavResource is to be created as a file + * or directory + * @see isCollection + */ + private boolean isCollectionRequest = false; + + public FSDavResource(FSDavResourceFactory factory + , DavResourceLocator locator, DavSession session + , ResourceConfig resourceConfig, Configuration conf + , boolean isCollectionRequest) + throws IOException { + + this.factory = factory; + this.locator = locator; + this.session = session; + this.conf = conf; + this.fs = FileSystem.get(conf); + String pathStr = locator.getResourcePath(); + if(pathStr.trim().equals("")) //emtpy path is not allowed + pathStr = "/"; + this.path = new Path(pathStr); + this.isCollectionRequest = isCollectionRequest; + } + + public FSDavResource(FSDavResourceFactory factory + , DavResourceLocator locator, DavSession session + , ResourceConfig resourceConfig, Configuration conf) throws IOException{ + + this(factory, locator,session, resourceConfig, conf, false); + } + + public String getComplianceClass() { + return COMPLIANCE_CLASS; + } + + private Path getPath() { + return path; + } + + public void addLockManager(LockManager lockmgr) { + this.lockManager = lockmgr; + } + + public void addMember(DavResource resource, InputContext inputContext) + throws DavException { + //A PUT performed on an existing resource replaces the GET response entity of the resource. Properties + //defined on the resource may be recomputed during PUT processing but are not otherwise affected. + Path destPath = ((FSDavResource)resource).getPath(); + try { + FSDavResource dfsResource = (FSDavResource)resource; + if(dfsResource.isCollectionRequest) { + LOG.debug("creating new directory : " + destPath.toUri().getPath()); + boolean success = fs.mkdirs(destPath); + if(!success) + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + else { + if(!inputContext.hasStream() || inputContext.getContentLength() < 0) { + LOG.debug("creating new file : " + destPath.toUri().getPath()); + boolean success = fs.createNewFile(destPath); + if(!success) + throw new DavException(DavServletResponse.SC_CONFLICT); + } + else { + LOG.debug("writing new file : " + destPath.toUri().getPath()); + OutputStream out = fs.create(destPath); + InputStream in = inputContext.getInputStream(); + IOUtils.copyBytes(in, out, conf, true); + } + } + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + public void removeMember(DavResource member) throws DavException { + try { + Path destPath = ((FSDavResource)member).getPath(); + boolean success = fs.delete(destPath); + if(!success) + throw new DavException(DavServletResponse.SC_NOT_FOUND); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + public void copy(DavResource destination, boolean shallow) + throws DavException { + + if (!exists()) { + throw new DavException(DavServletResponse.SC_NOT_FOUND); + } + if(!shallow || !isCollection()) { + FSDavResource dest = (FSDavResource)destination; + try { + FileUtil.copy(fs, path, fs,dest.getPath(), false, conf); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + // TODO: currently no support for shallow copy; however this is + // only relevant if the source resource is a collection, because + // otherwise it doesn't make a difference + throw new DavException(DavServletResponse.SC_FORBIDDEN, "Unable to perform shallow copy."); + } + + public void move(DavResource destination) throws DavException { + try { + fs.rename(path, ((FSDavResource)destination).getPath()); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + public boolean exists() { + try { + return fs.exists(path); + } + catch (IOException ex) { + // a DAV exception cannot be thrown + LOG.warn(StringUtils.stringifyException(ex)); + throw new RuntimeException(ex); + } + } + + public DavResource getCollection() { + if(path.depth() == 0) + return null; + + DavResourceLocator newLocator = locator.getFactory().createResourceLocator(locator.getPrefix() + , path.getParent().toUri().getPath()); + try { + return factory.createResource(newLocator, getSession()); + }catch (DavException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new RuntimeException(ex); + } + } + + public String getDisplayName() { + return path.getName(); + } + + public DavResourceFactory getFactory() { + return factory; + } + + public String getHref() { + return locator.getHref(isCollection()); + } + + public DavResourceLocator getLocator() { + return locator; + } + + public ActiveLock getLock(Type type, Scope scope) { + return lockManager.getLock(type, scope, this); + } + + public ActiveLock[] getLocks() { + return null; + } + + public DavResourceIterator getMembers() { + try { + Path[] children = fs.listPaths(new Path[] { path }); + ArrayList list = new ArrayList(); + if(children != null) { + for(Path p:children) { + DavResourceLocator resourceLocator + = locator.getFactory().createResourceLocator(locator.getPrefix() + , locator.getWorkspacePath(), p.toString(), false); + try { + list.add(factory.createResource(resourceLocator, getSession())); + } + catch (DavException ex) { + ex.printStackTrace(); + } + } + } + return new DavResourceIteratorImpl(list); + }catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new RuntimeException(ex); + } + } + + public long getModificationTime() { + try { + return fs.getFileStatus(path).getModificationTime(); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new RuntimeException(ex); + } + } + + + /*-------------------------------------------------------------------------*/ + /*---------------------------- Property Methods ---------------------------*/ + /*-------------------------------------------------------------------------*/ + + private void initProperties() { + if (inited) { + return; + } + + try { + FileStatus fstat = fs.getFileStatus(getPath()); + properties.add(new DefaultDavProperty(DavPropertyName.GETCONTENTLENGTH, fstat.getLen())); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + } + // set (or reset) fundamental properties + if (getDisplayName() != null) { + properties.add(new DefaultDavProperty(DavPropertyName.DISPLAYNAME, getDisplayName())); + } + if (isCollection()) { + properties.add(new ResourceType(ResourceType.COLLECTION)); + // Windows XP support + properties.add(new DefaultDavProperty(DavPropertyName.ISCOLLECTION, "1")); + } else { + properties.add(new ResourceType(ResourceType.DEFAULT_RESOURCE)); + // Windows XP support + properties.add(new DefaultDavProperty(DavPropertyName.ISCOLLECTION, "0")); + } + + //curtently no locking + /* set current lock information. If no lock is set to this resource, + an empty lockdiscovery will be returned in the response. */ + //properties.add(new LockDiscovery(getLock(Type.WRITE, Scope.EXCLUSIVE))); + + /* lock support information: all locks are lockable. */ + //SupportedLock supportedLock = new SupportedLock(); + //supportedLock.addEntry(Type.WRITE, Scope.EXCLUSIVE); + //properties.add(supportedLock); + + inited = true; + } + + + public DavPropertySet getProperties() { + initProperties(); + return properties; + } + + public DavProperty getProperty(DavPropertyName name) { + initProperties(); + return properties.get(name); + } + + public DavPropertyName[] getPropertyNames() { + initProperties(); + return properties.getPropertyNames(); + } + + public void removeProperty(DavPropertyName propertyName) throws DavException { + initProperties(); + properties.remove(propertyName); + } + + public void setProperty(DavProperty property) throws DavException { + initProperties(); + } + + public MultiStatusResponse alterProperties(List changeList) + throws DavException { + return null; + } + + @Deprecated + public MultiStatusResponse alterProperties(DavPropertySet setProperties, + DavPropertyNameSet removePropertyNames) throws DavException { + return null; + } + + //end of property methods + + public String getResourcePath() { + return locator.getResourcePath(); + } + + public DavSession getSession() { + return session; + } + + public String getSupportedMethods() { + return SUPPORTED_METHODS; + } + + public boolean hasLock(Type type, Scope scope) { + return false; + } + + public boolean isCollection() { + try { + return fs.getFileStatus(path).isDir(); + }catch (Exception ex) { + return false; + } + } + + public boolean isLockable(Type type, Scope scope) { + return false; + } + + public ActiveLock lock(LockInfo reqLockInfo) throws DavException { + return null; + } + + public void unlock(String lockToken) throws DavException { + } + + public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) + throws DavException { + return null; + } + + public void spool(OutputContext outputContext) throws IOException { + if (!isCollection()) { + InputStream input = fs.open(path); + try { + IOUtils.copyBytes(input, outputContext.getOutputStream(), conf, false); + } + finally { + input.close(); + } + } + } + +} Index: src/java/org/apache/hadoop/fs/webdav/WebdavServlet.java =================================================================== --- src/java/org/apache/hadoop/fs/webdav/WebdavServlet.java (revision 0) +++ src/java/org/apache/hadoop/fs/webdav/WebdavServlet.java (revision 0) @@ -0,0 +1,300 @@ +/** + * 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.hadoop.fs.webdav; + +import java.net.MalformedURLException; +import java.util.Enumeration; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.jackrabbit.server.AbstractWebdavServlet; +import org.apache.jackrabbit.webdav.DavLocatorFactory; +import org.apache.jackrabbit.webdav.DavResource; +import org.apache.jackrabbit.webdav.DavResourceFactory; +import org.apache.jackrabbit.webdav.DavSessionProvider; +import org.apache.jackrabbit.webdav.WebdavRequest; +import org.apache.jackrabbit.webdav.simple.LocatorFactoryImpl; +import org.apache.jackrabbit.webdav.simple.ResourceConfig; +import org.apache.jackrabbit.webdav.simple.ResourceFactoryImpl; + +public class WebdavServlet extends AbstractWebdavServlet { + + private static final long serialVersionUID = 1L; + + /** + * the default logger + */ + private static final Log log = LogFactory.getLog(WebdavServlet.class); + + /** + * init param name of the repository prefix + */ + public static final String INIT_PARAM_RESOURCE_PATH_PREFIX = "resource-path-prefix"; + + /** + * Name of the optional init parameter that defines the value of the + * 'WWW-Authenticate' header.

+ * If the parameter is omitted the default value + * {@link #DEFAULT_AUTHENTICATE_HEADER "Basic Realm=Hadoop Webdav Server"} + * is used. + * + * @see #getAuthenticateHeaderValue() + */ + public static final String INIT_PARAM_AUTHENTICATE_HEADER = "authenticate-header"; + + /** the 'missing-auth-mapping' init parameter */ + public final static String INIT_PARAM_MISSING_AUTH_MAPPING = "missing-auth-mapping"; + + /** + * Name of the init parameter that specify a separate configuration used + * for filtering the resources displayed. + */ + public static final String INIT_PARAM_RESOURCE_CONFIG = "resource-config"; + + /** + * Servlet context attribute used to store the path prefix instead of + * having a static field with this servlet. The latter causes problems + * when running multiple + */ + public static final String CTX_ATTR_RESOURCE_PATH_PREFIX = "hadoop.webdav.resourcepath"; + + /** + * the resource path prefix + */ + private String resourcePathPrefix; + + /** + * Header value as specified in the {@link #INIT_PARAM_AUTHENTICATE_HEADER} parameter. + */ + private String authenticate_header; + + /** + * the resource factory + */ + private DavResourceFactory resourceFactory; + + /** + * the locator factory + */ + private DavLocatorFactory locatorFactory; + + /** + * the webdav session provider + */ + private DavSessionProvider davSessionProvider; + + /** + * The config + */ + private ResourceConfig config; + + private static Configuration hadoopConfig = new Configuration(); + + /** + * Init this servlet + * + * @throws ServletException + */ + @Override + public void init() throws ServletException { + super.init(); + resourcePathPrefix = getInitParameter(INIT_PARAM_RESOURCE_PATH_PREFIX); + if (resourcePathPrefix == null) { + log.debug("Missing path prefix > setting to empty string."); + resourcePathPrefix = ""; + } else if (resourcePathPrefix.endsWith("/")) { + log.debug("Path prefix ends with '/' > removing trailing slash."); + resourcePathPrefix = resourcePathPrefix.substring(0, resourcePathPrefix.length() - 1); + } + getServletContext().setAttribute(CTX_ATTR_RESOURCE_PATH_PREFIX, resourcePathPrefix); + log.info(INIT_PARAM_RESOURCE_PATH_PREFIX + " = '" + resourcePathPrefix + "'"); + + authenticate_header = getInitParameter(INIT_PARAM_AUTHENTICATE_HEADER); + if (authenticate_header == null) { + authenticate_header = DEFAULT_AUTHENTICATE_HEADER; + } + log.info("WWW-Authenticate header = '" + authenticate_header + "'"); + + String configParam = getInitParameter(INIT_PARAM_RESOURCE_CONFIG); + if (configParam != null) { + try { + config = new ResourceConfig(); + config.parse(getServletContext().getResource(configParam)); + } catch (MalformedURLException e) { + log.debug("Unable to build resource filter provider."); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isPreconditionValid(WebdavRequest request, + DavResource resource) { + return !resource.exists() || request.matchesIfHeader(resource); + } + + /** + * Returns the DavLocatorFactory. If no locator factory has + * been set or created a new instance of {@link org.apache.jackrabbit.webdav.simple.LocatorFactoryImpl} is + * returned. + * + * @return the locator factory + * @see AbstractWebdavServlet#getLocatorFactory() + */ + @Override + public DavLocatorFactory getLocatorFactory() { + if (locatorFactory == null) { + locatorFactory = new LocatorFactoryImpl(resourcePathPrefix); + } + return locatorFactory; + } + + /** + * Sets the DavLocatorFactory. + * + * @param locatorFactory + * @see AbstractWebdavServlet#setLocatorFactory(DavLocatorFactory) + */ + @Override + public void setLocatorFactory(DavLocatorFactory locatorFactory) { + this.locatorFactory = locatorFactory; + } + + /** + * Returns the DavResourceFactory. If no request factory has + * been set or created a new instance of {@link ResourceFactoryImpl} is + * returned. + * + * @return the resource factory + * @see org.apache.jackrabbit.server.AbstractWebdavServlet#getResourceFactory() + */ + @Override + public DavResourceFactory getResourceFactory() { + if (resourceFactory == null) { + resourceFactory = new FSDavResourceFactory(getResourceConfig() + , getConf(getServletContext())); + } + return resourceFactory; + } + + /** + * Sets the DavResourceFactory. + * + * @param resourceFactory + * @see AbstractWebdavServlet#setResourceFactory(org.apache.jackrabbit.webdav.DavResourceFactory) + */ + @Override + public void setResourceFactory(DavResourceFactory resourceFactory) { + this.resourceFactory = resourceFactory; + } + + /** + * Returns the DavSessionProvider. If no session provider has + * been set or created a new instance of {@link FakeDavSessionProvider} + * is returned. + * + * @return the session provider + */ + @Override + public synchronized DavSessionProvider getDavSessionProvider() { + if (davSessionProvider == null) { + davSessionProvider = new FakeDavSessionProvider(); + } + return davSessionProvider; + } + + /** + * Sets the DavSessionProvider. + * + * @param sessionProvider + * @see AbstractWebdavServlet#setDavSessionProvider(org.apache.jackrabbit.webdav.DavSessionProvider) + */ + @Override + public synchronized void setDavSessionProvider(DavSessionProvider sessionProvider) { + this.davSessionProvider = sessionProvider; + } + + /** + * Returns the header value retrieved from the {@link #INIT_PARAM_AUTHENTICATE_HEADER} + * init parameter. If the parameter is missing, the value defaults to + * {@link #DEFAULT_AUTHENTICATE_HEADER}. + * + * @return the header value retrieved from the corresponding init parameter + * or {@link #DEFAULT_AUTHENTICATE_HEADER}. + * @see org.apache.jackrabbit.server.AbstractWebdavServlet#getAuthenticateHeaderValue() + */ + @Override + public String getAuthenticateHeaderValue() { + return authenticate_header; + } + + /** + * Returns the resource configuration to be applied + * + * @return the resource configuration. + */ + private ResourceConfig getResourceConfig() { + // fallback if no config present + if (config == null) { + config = new ResourceConfig(); + } + return config; + } + + /** + * Returns the caches {@link Configuration} if a {@link Configuration} is + * found in the {@link javax.servlet.ServletContext} it is simply returned, + * otherwise, a new {@link Configuration} is created, and then all the init + * parameters found in the {@link javax.servlet.ServletContext} are added to + * the {@link Configuration} (the created {@link Configuration} is then + * saved into the {@link javax.servlet.ServletContext}). + * + * @param application is the ServletContext whose init parameters + * must override those of Nutch. + */ + private static Configuration getConf(ServletContext application) { + Configuration conf = (Configuration) application.getAttribute("dfs.servlet.conf.key"); + if (conf == null) { + conf = hadoopConfig; + Enumeration e = application.getInitParameterNames(); + while (e.hasMoreElements()) { + String name = (String) e.nextElement(); + conf.set(name, application.getInitParameter(name)); + } + application.setAttribute("dfs.servlet.conf.key", conf); + } + + return conf; + } + + /** + * This is a cheesy way to set the value of the hadoop config + * @param config + */ + public static void setConf(Configuration config) { + hadoopConfig = config; + } + +} Index: bin/webdav.sh =================================================================== --- bin/webdav.sh (revision 0) +++ bin/webdav.sh (revision 0) @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +# +# The webdav command script +# +# Environment Variables +# +# JAVA_HOME The java implementation to use. Overrides JAVA_HOME. +# +# HADOOP_CLASSPATH Extra Java CLASSPATH entries. +# +# HADOOP_HEAPSIZE The maximum amount of heap to use, in MB. +# Default is 1000. +# +# HADOOP_OPTS Extra Java runtime options. +# +# HADOOP_CONF_DIR Alternate conf dir. Default is ${HADOOP_HOME}/conf. +# +# HADOOP_ROOT_LOGGER The root appender. Default is INFO,console +# + +bin=`dirname "$0"` +bin=`cd "$bin"; pwd` + +. "$bin"/hadoop-config.sh + +cygwin=false +case "`uname`" in +CYGWIN*) cygwin=true;; +esac + +if test $# -ge 1 ; then + if test $1 = '-h' -o $1 = '--help'; then + echo "Usage: webdav.sh [--config confdir] [--fs namenode:port] [--port port]" + echo " --config CONFDIR" + echo " The location of the configuration directory that should be read" + echo " --fs NAMENODE:PORT" + echo " The location of the namenode that the webdav server should serve" + echo " --port PORT" + echo " The port the webdav server should bind to" + exit 1 + fi +fi + +if [ -f "${HADOOP_CONF_DIR}/hadoop-env.sh" ]; then + . "${HADOOP_CONF_DIR}/hadoop-env.sh" +fi + +# some Java parameters +if [ "$JAVA_HOME" != "" ]; then + #echo "run java in $JAVA_HOME" + JAVA_HOME=$JAVA_HOME +fi + +if [ "$JAVA_HOME" = "" ]; then + echo "Error: JAVA_HOME is not set." + exit 1 +fi + +JAVA=$JAVA_HOME/bin/java +JAVA_HEAP_MAX=-Xmx1000m + +# check envvars which might override default args +if [ "$HADOOP_HEAPSIZE" != "" ]; then + #echo "run with heapsize $HADOOP_HEAPSIZE" + JAVA_HEAP_MAX="-Xmx""$HADOOP_HEAPSIZE""m" + #echo $JAVA_HEAP_MAX +fi + +# CLASSPATH initially contains $HADOOP_CONF_DIR +CLASSPATH="${HADOOP_CONF_DIR}" +CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar + +# for developers, add Hadoop classes to CLASSPATH +if [ -d "$HADOOP_HOME/build/classes" ]; then + CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build/classes +fi +if [ -d "$HADOOP_HOME/build/webapps" ]; then + CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build +fi +if [ -d "$HADOOP_HOME/build/test/classes" ]; then + CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build/test/classes +fi + +# so that filenames w/ spaces are handled correctly in loops below +IFS= + +# for releases, add core hadoop jar & webapps to CLASSPATH +if [ -d "$HADOOP_HOME/webapps" ]; then + CLASSPATH=${CLASSPATH}:$HADOOP_HOME +fi +for f in $HADOOP_HOME/hadoop-*-core.jar; do + CLASSPATH=${CLASSPATH}:$f; +done + +# add libs to CLASSPATH +for f in $HADOOP_HOME/lib/*.jar; do + CLASSPATH=${CLASSPATH}:$f; +done + +for f in $HADOOP_HOME/lib/*/*.jar; do + CLASSPATH=${CLASSPATH}:$f; +done + +# add user-specified CLASSPATH last +if [ "$HADOOP_CLASSPATH" != "" ]; then + CLASSPATH=${CLASSPATH}:${HADOOP_CLASSPATH} +fi + +# default log directory & file +if [ "$HADOOP_LOG_DIR" = "" ]; then + HADOOP_LOG_DIR="$HADOOP_HOME/logs" +fi +if [ "$HADOOP_LOGFILE" = "" ]; then + HADOOP_LOGFILE='hadoop.log' +fi + +# cygwin path translation +if $cygwin; then + CLASSPATH=`cygpath -p -w "$CLASSPATH"` + HADOOP_HOME=`cygpath -d "$HADOOP_HOME"` + HADOOP_LOG_DIR=`cygpath -d "$HADOOP_LOG_DIR"` +fi +# setup 'java.library.path' for native-hadoop code if necessary +JAVA_LIBRARY_PATH='' +if [ -d "${HADOOP_HOME}/build/native" -o -d "${HADOOP_HOME}/lib/native" ]; then + JAVA_PLATFORM=`CLASSPATH=${CLASSPATH} ${JAVA} org.apache.hadoop.util.PlatformName | sed -e "s/ /_/g"` + + if [ -d "$HADOOP_HOME/build/native" ]; then + JAVA_LIBRARY_PATH=${HADOOP_HOME}/build/native/${JAVA_PLATFORM}/lib + fi + + if [ -d "${HADOOP_HOME}/lib/native" ]; then + if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then + JAVA_LIBRARY_PATH=${JAVA_LIBRARY_PATH}:${HADOOP_HOME}/lib/native/${JAVA_PLATFORM} + else + JAVA_LIBRARY_PATH=${HADOOP_HOME}/lib/native/${JAVA_PLATFORM} + fi + fi +fi + +# cygwin path translation +if $cygwin; then + JAVA_LIBRARY_PATH=`cygpath -p "$JAVA_LIBRARY_PATH"` +fi + +# restore ordinary behaviour +unset IFS + +HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.log.dir=$HADOOP_LOG_DIR" +HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.log.file=$HADOOP_LOGFILE" +HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.home.dir=$HADOOP_HOME" +HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.id.str=$HADOOP_IDENT_STRING" +HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.root.logger=${HADOOP_ROOT_LOGGER:-INFO,console}" +if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then + HADOOP_OPTS="$HADOOP_OPTS -Djava.library.path=$JAVA_LIBRARY_PATH" +fi + +# run it +exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS -classpath "$CLASSPATH" org.apache.hadoop.fs.webdav "$@"