Index: src/java/org/apache/hadoop/mapred/StatusHttpServer.java =================================================================== --- src/java/org/apache/hadoop/mapred/StatusHttpServer.java (revision 595154) +++ src/java/org/apache/hadoop/mapred/StatusHttpServer.java (working copy) @@ -22,6 +22,7 @@ import java.io.PrintWriter; import java.net.URL; +import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -44,12 +45,13 @@ * "/" -> the jsp server code from (src/webapps/) */ public class StatusHttpServer { + @SuppressWarnings("unused") private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); - private org.mortbay.jetty.Server webServer; + protected org.mortbay.jetty.Server webServer; private SocketListener listener; private boolean findPort; - private WebApplicationContext webAppContext; + protected WebApplicationContext webAppContext; private static final Log LOG = LogFactory.getLog(StatusHttpServer.class.getName()); @@ -63,13 +65,8 @@ */ public StatusHttpServer(String name, String bindAddress, int port, boolean findPort) throws IOException { - webServer = new org.mortbay.jetty.Server(); - this.findPort = findPort; - listener = new SocketListener(); - listener.setPort(port); - listener.setHost(bindAddress); - webServer.addListener(listener); - + + this(bindAddress, port, findPort); // set up the context for "/logs/" if "hadoop.log.dir" property is defined. String logDir = System.getProperty("hadoop.log.dir"); if (logDir != null) { @@ -79,7 +76,7 @@ logContext.addHandler(new ResourceHandler()); webServer.addContext(logContext); } - + // set up the context for "/static/*" String appDir = getWebAppsPath(); HttpContext staticContext = new HttpContext(); @@ -97,6 +94,16 @@ org.apache.hadoop.log.LogLevel.Servlet.class); } + protected StatusHttpServer(String bindAddress, int port, boolean findPort) + throws IOException { + webServer = new org.mortbay.jetty.Server(); + this.findPort = findPort; + listener = new SocketListener(); + listener.setPort(port); + listener.setHost(bindAddress); + webServer.addListener(listener); + } + /** * Set a value in the webapp context. These values are available to the jsp * pages as "application.getAttribute(name)". @@ -113,7 +120,7 @@ * @param pathSpec The path spec for the servlet * @param servletClass The servlet class */ - public + public void addServlet(String name, String pathSpec, Class servletClass) { @@ -155,7 +162,7 @@ * Get the pathname to the webapps files. * @return the pathname as a URL */ - private static String getWebAppsPath() throws IOException { + protected static String getWebAppsPath() throws IOException { URL url = StatusHttpServer.class.getClassLoader().getResource("webapps"); if (url == null) throw new IOException("webapps not found in CLASSPATH"); Index: src/java/org/apache/hadoop/dfs/webdav/DFSDavResource.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/DFSDavResource.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/DFSDavResource.java (revision 0) @@ -0,0 +1,465 @@ +/** + * 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.dfs.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.server.io.PropertyManager; +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 DFSDavResource implements DavResource { + + private static final Log LOG = LogFactory.getLog(DFSDavResource.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 DFSDavResourceFactory factory; + private final DavResourceLocator locator; + private LockManager lockManager; + private DavSession session; + + private DavPropertySet properties = new DavPropertySet(); + private PropertyManager propertyManager; + + //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 DFSDavResource(DFSDavResourceFactory 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.propertyManager = resourceConfig.getPropertyManager(); + this.isCollectionRequest = isCollectionRequest; + } + + public DFSDavResource(DFSDavResourceFactory factory + , DavResourceLocator locator, DavSession session + , ResourceConfig resourceConfig, Configuration conf) throws IOException{ + + this(factory, locator,session, resourceConfig, conf, false); + } + + public String getComplianceClass() { + return COMPLIANCE_CLASS; + } + + public Path getPath() { + return path; + } + + public void addLockManager(LockManager lockmgr) { + LOG.info("addLockManager(LockManager)"); + this.lockManager = lockmgr; + } + + public void addMember(DavResource resource, InputContext inputContext) + throws DavException { + LOG.info("addMember(DavResource, InputContext) for path:" + path.toUri().getPath()); + //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 = ((DFSDavResource)resource).getPath(); + try { + DFSDavResource dfsResource = (DFSDavResource)resource; + if(dfsResource.isCollectionRequest) { + LOG.info("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.info("creating new file : " + destPath.toUri().getPath()); + boolean success = fs.createNewFile(destPath); + if(!success) + throw new DavException(DavServletResponse.SC_CONFLICT); + } + else { + LOG.info("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 { + LOG.info("removeMember(DavResource)"); + try { + Path destPath = ((DFSDavResource)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 { + + LOG.info("copy(DavResource, boolean)"); + if (!exists()) { + throw new DavException(DavServletResponse.SC_NOT_FOUND); + } + if(!shallow || !isCollection()) { + DFSDavResource dest = (DFSDavResource)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 { + LOG.info("move()"); + try { + fs.rename(path, ((DFSDavResource)destination).getPath()); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + public boolean exists() { + LOG.info("exists(" + locator.getResourcePath() + ")"); + 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() { + LOG.info("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() { + LOG.info("getDisplayName()"); + return path.getName(); + } + + public DavResourceFactory getFactory() { + return factory; + } + + public String getHref() { + LOG.info("getHref() for path:" + path.toUri().getPath()); + return locator.getHref(isCollection()); + } + + public DavResourceLocator getLocator() { + LOG.info("getLocator()"); + return locator; + } + + public ActiveLock getLock(Type type, Scope scope) { + LOG.info("getLock(Type, Scope)"); + return lockManager.getLock(type, scope, this); + } + + public ActiveLock[] getLocks() { + LOG.info("getLock()"); + return null; + } + + public DavResourceIterator getMembers() { + LOG.info("getMembers()"); + try { + Path[] children = fs.listPaths(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() { + LOG.info("getModificationTime()"); + + try { + return fs.getFileStatus(path).getModificationTime(); + } + catch (IOException ex) { + LOG.warn(StringUtils.stringifyException(ex)); + throw new RuntimeException(ex); + } + } + + + /*-------------------------------------------------------------------------*/ + /*---------------------------- Property Methods ---------------------------*/ + /*-------------------------------------------------------------------------*/ + + protected void initProperties() { + if (inited) { + return; + } + +// try { +// propManager.exportProperties(getPropertyExportContext(), isCollection()); +// } catch (RepositoryException e) { +// log.warn("Error while accessing resource properties", e); +// } + 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() { + LOG.info("getProperties() for path: " + path.toUri().getPath()); + initProperties(); + return properties; + } + + public DavProperty getProperty(DavPropertyName name) { + LOG.info("getProperty()"); + initProperties(); + return properties.get(name); + } + + public DavPropertyName[] getPropertyNames() { + LOG.info("getPropertyNames()"); + initProperties(); + return properties.getPropertyNames(); + } + + public void removeProperty(DavPropertyName propertyName) throws DavException { + LOG.info("removeProperty()"); + initProperties(); + properties.remove(propertyName); + } + + public void setProperty(DavProperty property) throws DavException { + LOG.info("setProperty(DavProperty)"); + initProperties(); + } + + @SuppressWarnings("unchecked") + public MultiStatusResponse alterProperties(List changeList) + throws DavException { + return null; + } + + public MultiStatusResponse alterProperties(DavPropertySet setProperties, + DavPropertyNameSet removePropertyNames) throws DavException { + return null; + } + + //end of property methods + + public String getResourcePath() { + LOG.info("getResourcePath()"); + return locator.getResourcePath(); + } + + public DavSession getSession() { + LOG.info("getSession()"); + return session; + } + + public String getSupportedMethods() { + LOG.info("getSupportedMethods()"); + return SUPPORTED_METHODS; + } + + public boolean hasLock(Type type, Scope scope) { + LOG.info("hasLock()"); + return false; + } + + public boolean isCollection() { + LOG.info("isCollection() for path: " + path.toUri().getPath()); + try { + return fs.getFileStatus(path).isDir(); + }catch (Exception ex) { + return false; + } + } + + public boolean isLockable(Type type, Scope scope) { + LOG.info("isLockable(Type, Scope)"); + return false; + } + + public ActiveLock lock(LockInfo reqLockInfo) throws DavException { + LOG.info("lock(LockInfo)"); + return null; + } + + public void unlock(String lockToken) throws DavException { + LOG.info("unlock(String lockToken)"); + } + + public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) + throws DavException { + LOG.info("refreshLock(LockInfo, String lockToken)"); + return null; + } + + public void spool(OutputContext outputContext) throws IOException { + LOG.info("spool()"); + if (!isCollection()) { + InputStream input = fs.open(path); + try { + IOUtils.copyBytes(input, outputContext.getOutputStream(), conf, false); + } + finally { + input.close(); + } + } + } + +} Index: src/java/org/apache/hadoop/dfs/webdav/WebdavServer.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/WebdavServer.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/WebdavServer.java (revision 0) @@ -0,0 +1,59 @@ +/** + * 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.dfs.webdav; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.mapred.StatusHttpServer; +import org.mortbay.jetty.servlet.WebApplicationContext; + +public class WebdavServer extends StatusHttpServer{ + + private static final Log LOG = LogFactory.getLog(WebdavServer.class); + + /** @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, + boolean findPort) throws IOException { + super(bindAddress, port, findPort); + LOG.info("Initializing webdav server"); + webAppContext = new WebApplicationContext(); + webAppContext.setContextPath("/"); + addServlet(null,"/*", WebdavServlet.class); + webServer.addContext(webAppContext); + } + + public static void main(String[] args) { + + try { + WebdavServer server = new WebdavServer("localhost", 20015, false); + LOG.info("Starting webdav server"); + server.start(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + +} Index: src/java/org/apache/hadoop/dfs/webdav/FakeDavSessionProvider.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/FakeDavSessionProvider.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/FakeDavSessionProvider.java (revision 0) @@ -0,0 +1,40 @@ +/** + * 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.dfs.webdav; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.DavSessionProvider; +import org.apache.jackrabbit.webdav.WebdavRequest; + +public class FakeDavSessionProvider implements DavSessionProvider { + + private static final Log LOG = LogFactory.getLog(FakeDavSessionProvider.class); + + public boolean attachSession(WebdavRequest request) throws DavException { + LOG.info("Attach session request"); + return true; + } + + public void releaseSession(WebdavRequest request) { + LOG.info("Release session request"); + } + +} Index: src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceLocator.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceLocator.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceLocator.java (revision 0) @@ -0,0 +1,69 @@ +/** + * 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.dfs.webdav; + +import org.apache.jackrabbit.webdav.DavLocatorFactory; +import org.apache.jackrabbit.webdav.DavResourceLocator; + +public class DFSDavResourceLocator implements DavResourceLocator { + + public DFSDavResourceLocator() { + } + + public DavLocatorFactory getFactory() { + return null; + } + + public String getHref(boolean isCollection) { + return null; + } + + public String getPrefix() { + return null; + } + + public String getRepositoryPath() { + return null; + } + + public String getResourcePath() { + return null; + } + + public String getWorkspaceName() { + return null; + } + + public String getWorkspacePath() { + return null; + } + + public boolean isRootLocation() { + return false; + } + + public boolean isSameWorkspace(DavResourceLocator locator) { + return false; + } + + public boolean isSameWorkspace(String workspaceName) { + return false; + } + +} Index: src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceFactory.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceFactory.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/DFSDavResourceFactory.java (revision 0) @@ -0,0 +1,77 @@ +/** + * 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.dfs.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.lock.LockManager; +import org.apache.jackrabbit.webdav.simple.ResourceConfig; + +public class DFSDavResourceFactory implements DavResourceFactory { + + private static final Log LOG = LogFactory.getLog(DFSDavResourceFactory.class); + + private final LockManager lockMgr; + private final ResourceConfig resourceConfig; + private final Configuration conf; + + public DFSDavResourceFactory(LockManager lockMgr, ResourceConfig resourceConfig + , Configuration conf) { + this.lockMgr = lockMgr; + this.resourceConfig = resourceConfig; + this.conf = conf; + } + + public DavResource createResource(DavResourceLocator locator, + DavSession session) throws DavException { + + LOG.info("createResource(DavResourceLocator, DavSession)"); + try { + return new DFSDavResource(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.info("createResource(DavResourceLocator, DavServletRequest, DavServletResponse)"); + try { + return new DFSDavResource(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/dfs/webdav/WebdavServlet.java =================================================================== --- src/java/org/apache/hadoop/dfs/webdav/WebdavServlet.java (revision 0) +++ src/java/org/apache/hadoop/dfs/webdav/WebdavServlet.java (revision 0) @@ -0,0 +1,402 @@ +/** + * 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.dfs.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.server.BasicCredentialsProvider; +import org.apache.jackrabbit.server.CredentialsProvider; +import org.apache.jackrabbit.server.SessionProvider; +import org.apache.jackrabbit.server.SessionProviderImpl; +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.lock.LockManager; +import org.apache.jackrabbit.webdav.lock.SimpleLockManager; +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"; + + public static final String DEFAULT_AUTHENTICATE_HEADER = "Basic realm=\"Hadoop Webdav Server\""; + + /** + * the resource path prefix + */ + private String resourcePathPrefix; + + /** + * Header value as specified in the {@link #INIT_PARAM_AUTHENTICATE_HEADER} parameter. + */ + private String authenticate_header; + + /** + * Map used to remember any webdav lock created without being reflected + * in the underlying repository. + * This is needed because some clients rely on a successful locking + * mechanism in order to perform properly (e.g. mac OSX built-in dav client) + */ + private LockManager lockManager; + + /** + * the resource factory + */ + private DavResourceFactory resourceFactory; + + /** + * the locator factory + */ + private DavLocatorFactory locatorFactory; + + /** + * the webdav session provider + */ + private DavSessionProvider davSessionProvider; + + /** + * the repository session provider + */ + private SessionProvider sessionProvider; + + /** + * The config + */ + private ResourceConfig config; + + /** + * 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 configured path prefix + * + * @return resourcePathPrefix + * @see #INIT_PARAM_RESOURCE_PATH_PREFIX + */ + public String getPathPrefix() { + return resourcePathPrefix; + } + + /** + * Returns the configured path prefix + * + * @return resourcePathPrefix + * @see #INIT_PARAM_RESOURCE_PATH_PREFIX + */ + public static String getPathPrefix(ServletContext ctx) { + return (String) ctx.getAttribute(CTX_ATTR_RESOURCE_PATH_PREFIX); + } + + /** + * 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 LockManager. If no lock manager has + * been set or created a new instance of {@link SimpleLockManager} is + * returned. + * + * @return the lock manager + */ + public LockManager getLockManager() { + if (lockManager == null) { + lockManager = new SimpleLockManager(); + } + return lockManager; + } + + /** + * Sets the LockManager. + * + * @param lockManager + */ + public void setLockManager(LockManager lockManager) { + this.lockManager = lockManager; + } + + /** + * 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 DFSDavResourceFactory(getLockManager(), 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 SessionProvider. If no session provider has been + * set or created a new instance of {@link SessionProviderImpl} that extracts + * credentials from the request's Authorization header is + * returned. + * + * @return the session provider + */ + public synchronized SessionProvider getSessionProvider() { + if (sessionProvider == null) { + sessionProvider = new SessionProviderImpl(getCredentialsProvider()); + } + return sessionProvider; + } + + /** + * Factory method for creating the credentials provider to be used for + * accessing the credentials associated with a request. The default + * implementation returns a {@link BasicCredentialsProvider} instance, + * but subclasses can override this method to add support for other + * types of credentials. + * + * @return the credentilas provider + * @since 1.3 + */ + protected CredentialsProvider getCredentialsProvider() { + return new BasicCredentialsProvider(getInitParameter(INIT_PARAM_MISSING_AUTH_MAPPING)); + } + + /** + * Sets the SessionProvider. + * + * @param sessionProvider + */ + public synchronized void setSessionProvider(SessionProvider sessionProvider) { + this.sessionProvider = sessionProvider; + } + + /** + * 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. + */ + public ResourceConfig getResourceConfig() { + // fallback if no config present + if (config == null) { + config = new ResourceConfig(); + } + return config; + } + + /** + * Set the resource configuration + * + * @param config + */ + public void setResourceConfig(ResourceConfig config) { + this.config = 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. + */ + @SuppressWarnings("unchecked") + public static Configuration getConf(ServletContext application) { + Configuration conf = (Configuration) application.getAttribute("dfs.servlet.conf.key"); + if (conf == null) { + conf = new Configuration(); + 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; + } + +} Index: bin/hadoop =================================================================== --- bin/hadoop (revision 595154) +++ bin/hadoop (working copy) @@ -43,6 +43,7 @@ echo " pipes run a Pipes job" echo " tasktracker run a MapReduce task Tracker node" echo " job manipulate MapReduce jobs" + echo " webdav run the webdav server" echo " version print the version" echo " jar run a jar file" echo " distcp copy file or directories recursively" @@ -113,7 +114,7 @@ CLASSPATH=${CLASSPATH}:$f; done -for f in $HADOOP_HOME/lib/jetty-ext/*.jar; do +for f in $HADOOP_HOME/lib/*/*.jar; do CLASSPATH=${CLASSPATH}:$f; done @@ -183,6 +184,8 @@ CLASS=org.apache.hadoop.mapred.TaskTracker elif [ "$COMMAND" = "job" ] ; then CLASS=org.apache.hadoop.mapred.JobClient +elif [ "$COMMAND" = "webdav" ] ; then + CLASS=org.apache.hadoop.dfs.webdav.WebdavServer elif [ "$COMMAND" = "pipes" ] ; then CLASS=org.apache.hadoop.mapred.pipes.Submitter elif [ "$COMMAND" = "version" ] ; then