// *************************************************************************************************************************** | |
// * 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.juneau.examples.rest; | |
import static java.util.logging.Level.*; | |
import static javax.servlet.http.HttpServletResponse.*; | |
import static org.apache.juneau.html.HtmlSerializer.*; | |
import static org.apache.juneau.http.HttpMethodName.*; | |
import static org.apache.juneau.rest.annotation.HookEvent.*; | |
import java.io.*; | |
import java.net.*; | |
import java.util.*; | |
import java.util.logging.*; | |
import org.apache.juneau.rest.*; | |
import org.apache.juneau.rest.annotation.*; | |
import org.apache.juneau.rest.converters.*; | |
import org.apache.juneau.rest.widget.*; | |
import org.apache.juneau.utils.*; | |
/** | |
* Sample REST resource for exploring local file systems. | |
*/ | |
@RestResource( | |
messages="nls/DirectoryResource", | |
htmldoc=@HtmlDoc( | |
widgets={ | |
ContentTypeMenuItem.class, | |
StyleMenuItem.class | |
}, | |
navlinks={ | |
"up: request:/..", | |
"options: servlet:/?method=OPTIONS", | |
"$W{ContentTypeMenuItem}", | |
"$W{StyleMenuItem}", | |
"source: $C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java" | |
} | |
), | |
allowedMethodParams="*", | |
properties={ | |
@Property(name=HTML_uriAnchorText, value="PROPERTY_NAME"), | |
@Property(name="rootDir", value="$S{java.io.tmpdir}"), | |
@Property(name="allowViews", value="false"), | |
@Property(name="allowDeletes", value="false"), | |
@Property(name="allowPuts", value="false") | |
} | |
) | |
public class DirectoryResource extends BasicRestServlet { | |
private static final long serialVersionUID = 1L; | |
private File rootDir; // The root directory | |
// Settings enabled through servlet init parameters | |
boolean allowDeletes, allowPuts, allowViews; | |
private static Logger logger = Logger.getLogger(DirectoryResource.class.getName()); | |
@RestHook(INIT) | |
public void init(RestContextBuilder builder) throws Exception { | |
RestContextProperties p = builder.getProperties(); | |
rootDir = new File(p.getString("rootDir")); | |
allowViews = p.getBoolean("allowViews", false); | |
allowDeletes = p.getBoolean("allowDeletes", false); | |
allowPuts = p.getBoolean("allowPuts", false); | |
} | |
/** Returns the root directory defined by the 'rootDir' init parameter */ | |
protected File getRootDir() { | |
if (rootDir == null) { | |
rootDir = new File(getProperties().getString("rootDir")); | |
if (! rootDir.exists()) | |
if (! rootDir.mkdirs()) | |
throw new RuntimeException("Could not create root dir"); | |
} | |
return rootDir; | |
} | |
/** GET request handler */ | |
@RestMethod(name=GET, path="/*", converters={Queryable.class}) | |
public Object doGet(RestRequest req, RequestProperties properties) throws Exception { | |
String pathInfo = req.getPathInfo(); | |
File f = pathInfo == null ? rootDir : new File(rootDir.getAbsolutePath() + pathInfo); | |
if (!f.exists()) | |
throw new RestException(SC_NOT_FOUND, "File not found"); | |
properties.put("path", f.getAbsolutePath()); | |
if (f.isDirectory()) { | |
List<FileResource> l = new LinkedList<>(); | |
File[] lfc = f.listFiles(); | |
if (lfc != null) { | |
for (File fc : lfc) { | |
URL fUrl = new URL(req.getRequestURL().append("/").append(fc.getName()).toString()); | |
l.add(new FileResource(fc, fUrl)); | |
} | |
} | |
return l; | |
} | |
return new FileResource(f, new URL(req.getRequestURL().toString())); | |
} | |
/** DELETE request handler */ | |
@RestMethod(name=DELETE, path="/*", guards=AdminGuard.class) | |
public Object doDelete(RestRequest req) throws Exception { | |
if (! allowDeletes) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "DELETE not enabled"); | |
File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); | |
deleteFile(f); | |
if (req.getHeader("Accept").contains("text/html")) | |
return new Redirect(); | |
return "File deleted"; | |
} | |
/** PUT request handler */ | |
@RestMethod(name=PUT, path="/*", guards=AdminGuard.class) | |
public Object doPut(RestRequest req) throws Exception { | |
if (! allowPuts) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "PUT not enabled"); | |
File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); | |
String parentSubPath = f.getParentFile().getAbsolutePath().substring(rootDir.getAbsolutePath().length()); | |
try (InputStream is = req.getInputStream(); OutputStream os = new BufferedOutputStream(new FileOutputStream(f))) { | |
IOPipe.create(is, os).run(); | |
} | |
if (req.getContentType().contains("html")) | |
return new Redirect(parentSubPath); | |
return "File added"; | |
} | |
/** VIEW request handler (overloaded GET for viewing file contents) */ | |
@SuppressWarnings("resource") | |
@RestMethod(name="VIEW", path="/*") | |
public void doView(RestRequest req, RestResponse res) throws Exception { | |
if (! allowViews) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "VIEW not enabled"); | |
File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); | |
if (!f.exists()) | |
throw new RestException(SC_NOT_FOUND, "File not found"); | |
if (f.isDirectory()) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "VIEW not available on directories"); | |
res.setOutput(new FileReader(f)).setContentType("text/plain"); | |
} | |
/** DOWNLOAD request handler (overloaded GET for downloading file contents) */ | |
@SuppressWarnings("resource") | |
@RestMethod(name="DOWNLOAD") | |
public void doDownload(RestRequest req, RestResponse res) throws Exception { | |
if (! allowViews) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "DOWNLOAD not enabled"); | |
File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); | |
if (!f.exists()) | |
throw new RestException(SC_NOT_FOUND, "File not found"); | |
if (f.isDirectory()) | |
throw new RestException(SC_METHOD_NOT_ALLOWED, "DOWNLOAD not available on directories"); | |
res.setOutput(new FileReader(f)).setContentType("application"); | |
} | |
/** File POJO */ | |
public class FileResource { | |
private File f; | |
private URL url; | |
/** Constructor */ | |
public FileResource(File f, URL url) { | |
this.f = f; | |
this.url = url; | |
} | |
// Bean property getters | |
public URL getUrl() { | |
return url; | |
} | |
public String getType() { | |
return (f.isDirectory() ? "dir" : "file"); | |
} | |
public String getName() { | |
return f.getName(); | |
} | |
public long getSize() { | |
return f.length(); | |
} | |
public Date getLastModified() { | |
return new Date(f.lastModified()); | |
} | |
public URL getView() throws Exception { | |
if (allowViews && f.canRead() && ! f.isDirectory()) | |
return new URL(url + "?method=VIEW"); | |
return null; | |
} | |
public URL getDownload() throws Exception { | |
if (allowViews && f.canRead() && ! f.isDirectory()) | |
return new URL(url + "?method=DOWNLOAD"); | |
return null; | |
} | |
public URL getDelete() throws Exception { | |
if (allowDeletes && f.canWrite()) | |
return new URL(url + "?method=DELETE"); | |
return null; | |
} | |
} | |
/** Utility method */ | |
private void deleteFile(File f) { | |
try { | |
if (f.isDirectory()) { | |
File[] lfc = f.listFiles(); | |
if (lfc != null) | |
for (File fc : lfc) | |
deleteFile(fc); | |
} | |
f.delete(); | |
} catch (Exception e) { | |
logger.log(WARNING, "Cannot delete file '" + f.getAbsolutePath() + "'", e); | |
} | |
} | |
} |