Java tutorial
/* * R Service Bus * * Copyright (c) Copyright of Open Analytics NV, 2010-2015 * * =========================================================================== * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.openanalytics.rsb.component; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.PostConstruct; import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Component; import org.springframework.util.DigestUtils; import eu.openanalytics.rsb.Constants; import eu.openanalytics.rsb.Util; import eu.openanalytics.rsb.rest.types.Directory; import eu.openanalytics.rsb.rest.types.FileType; /** * @author "OpenAnalytics <rsb.development@openanalytics.eu>" */ @Component("dataDirectoriesResource") @Path("/" + Constants.DATA_DIR_PATH) // Produces "regular" XML/JSON content types as browsers don't understand subtypes // correctly @Produces({ Constants.JSON_CONTENT_TYPE, Constants.XML_CONTENT_TYPE, Constants.RSB_XML_CONTENT_TYPE, Constants.RSB_JSON_CONTENT_TYPE }) public class DataDirectoriesResource extends AbstractResource { private final Map<String, File> rootMap = new HashMap<String, File>(); // exposed for unit testing Map<String, File> getRootMap() { return rootMap; } @PostConstruct public void setupRootMap() throws IOException { final List<File> dataDirectoryRoots = getConfiguration().getDataDirectories(); if (dataDirectoryRoots != null) { for (final File dataDirectoryRoot : dataDirectoryRoots) { final String rootKey = Base64.encodeBase64URLSafeString( DigestUtils.md5Digest(dataDirectoryRoot.getCanonicalPath().getBytes())); rootMap.put(rootKey, dataDirectoryRoot); } } } @Path("/") @GET public Directory browseRoots(@Context final HttpHeaders httpHeaders, @Context final UriInfo uriInfo) throws URISyntaxException, IOException { final Directory roots = Util.REST_OBJECT_FACTORY.createDirectory(); roots.setPath("/"); roots.setName("Remote Data"); roots.setUri(Util.buildDataDirectoryUri(httpHeaders, uriInfo, "/").toString()); roots.setEmpty(rootMap.isEmpty()); for (final Entry<String, File> rootEntry : rootMap.entrySet()) { final Directory root = Util.REST_OBJECT_FACTORY.createDirectory(); final File rootDirectory = rootEntry.getValue(); root.setPath(rootDirectory.getCanonicalPath()); root.setName(rootDirectory.getName()); root.setUri(Util.buildDataDirectoryUri(httpHeaders, uriInfo, rootEntry.getKey()).toString()); root.setEmpty(isDirectoryEmpty(rootDirectory)); roots.getDirectories().add(root); } return roots; } @Path("/{rootId}{b64extension : (/b64extension)?}") @GET public Directory browsePath(@PathParam("rootId") final String rootId, @PathParam("b64extension") final String b64extension, @Context final HttpHeaders httpHeaders, @Context final UriInfo uriInfo) throws URISyntaxException, IOException { final File rootDataDir = rootMap.get(rootId); if (rootDataDir == null) { throw new NotFoundException(new RuntimeException("No root data dir configured")); } final String extension = (b64extension != null ? new String(Base64.decodeBase64(b64extension)) : ""); final File targetDataDir = new File(rootDataDir, extension); if (!targetDataDir.exists()) { throw new NotFoundException(new RuntimeException("Invalid root data dir: " + targetDataDir)); } // ensure the target data dir is below the root dir to prevent tampering final String rootDataDirCanonicalPath = rootDataDir.getCanonicalPath(); final String targetDataDirCanonicalPath = targetDataDir.getCanonicalPath(); if (!StringUtils.startsWith(targetDataDirCanonicalPath, rootDataDirCanonicalPath)) { throw new AccessDeniedException("Target data dir: " + targetDataDirCanonicalPath + " is not below root dir: " + rootDataDirCanonicalPath); } final Directory result = Util.REST_OBJECT_FACTORY.createDirectory(); result.setPath(rootDataDirCanonicalPath + extension); result.setName(targetDataDir.getName()); result.setUri(Util.buildDataDirectoryUri(httpHeaders, uriInfo, rootId, b64extension).toString()); final File[] targetDataDirFiles = targetDataDir.listFiles(); result.setEmpty(targetDataDirFiles.length == 0); for (final File child : targetDataDirFiles) { if (FileUtils.isSymlink(child)) { getLogger().warn("Symlinks are not supported: " + child); } else if (child.isFile()) { final FileType fileType = Util.REST_OBJECT_FACTORY.createFileType(); fileType.setPath(child.getCanonicalPath()); fileType.setName(child.getName()); result.getFiles().add(fileType); } else if (child.isDirectory()) { final Directory childDir = Util.REST_OBJECT_FACTORY.createDirectory(); childDir.setPath(child.getCanonicalPath()); childDir.setName(child.getName()); final String childB64extension = Base64.encodeBase64URLSafeString( StringUtils.difference(rootDataDirCanonicalPath, child.getCanonicalPath()).getBytes()); childDir.setUri( Util.buildDataDirectoryUri(httpHeaders, uriInfo, rootId, childB64extension).toString()); childDir.setEmpty(isDirectoryEmpty(child)); result.getDirectories().add(childDir); } else { getLogger().warn("Unsupported file type: " + child); } } return result; } private boolean isDirectoryEmpty(final File file) { if (!file.isDirectory()) { return true; } final File[] childFiles = file.listFiles(); return childFiles == null ? true : childFiles.length == 0; } }