/* * Copyright (c) WSO2 Inc. (http://wso2.com) All Rights Reserved. * * WSO2 Inc. 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.wso2.carbon.registry.extensions.handlers.scm; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.registry.core.*; import org.wso2.carbon.registry.core.Collection; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.jdbc.handlers.Handler; import org.wso2.carbon.registry.core.jdbc.handlers.RequestContext; import org.wso2.carbon.registry.core.utils.RegistryUtils; import org.wso2.carbon.registry.extensions.utils.CommonUtil; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.*; public class ExternalContentHandler extends Handler { private static final Log log = LogFactory.getLog(ExternalContentHandler.class); private FilesystemManager filesystemManager; private String mountPath; private volatile boolean busy; public void setFilePath(String filePath) { this.filesystemManager = new FilesystemManager(filePath); } public void setMountPath(String mountPath) { this.mountPath = mountPath; } public void setBusy(boolean busy) { this.busy = busy; } private void validateUpdateInProgress() throws RegistryException { if (busy) { throw new RegistryException("An update is currently in progress. Please try again " + "later."); } } // Operations: CRUD // New Repo: Create File/Folder based on type of resource. If files can't be created throw an // exception. // Existing Repo: If the destination is a folder, build a collection at that location, and // recursively call get for each child. If the destination is a file, just load // the content and create a resource at that location. // Moving, Copying: Compare source and destination with Base (need some way to specify base). // Call create/delete depending on whether the src/dest is on repo. // Dump, Restore needs to be handled properly. // Try to use ContentStreaming with an OnDemand resource to avoid caching overhead. // Updating the repo is a responsibility of the implementation and should use a task/thread for // that. Same applies for sync'ing. // Create a OperationContext to pass info (ex:- Username, Time) to the implementation. This // class will provide constants for key names. // This will always use a file-system back-up irrespective of the implementation. // Try to use a SCM configuration in registry.xml to make life easy (copy from maven). // http://svn.apache.org/repos/asf/maven/scm/trunk/maven-scm-client/src/main/java/org/apache/maven/scm/client/cli/MavenScmCli.java public Resource get(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isSCMLockAvailable()) { return null; } CommonUtil.acquireSCMLock(); try { Registry registry = requestContext.getRegistry(); Resource resource = registry.get(requestContext.getResourcePath().getPath()); String path = RegistryUtils.getRelativePathToOriginal( requestContext.getResourcePath().getPath(), mountPath); if (resource instanceof Collection) { CollectionImpl collection = (CollectionImpl) resource; String[] children = filesystemManager.getDirectoryContent(path); List<String> temp = new LinkedList<String>(); for (String child : children) { if (path.equals(RegistryConstants.PATH_SEPARATOR)) { temp.add(mountPath + RegistryConstants.PATH_SEPARATOR + child); } else { temp.add(mountPath + path + RegistryConstants.PATH_SEPARATOR + child); } } children = temp.toArray(new String[temp.size()]); if (collection.getChildCount() != children.length) { // if there is a difference in child counts, fix it. if (collection.getChildCount() > 0) { List<String> pathsToDelete = new ArrayList<String>( Arrays.asList(collection.getChildren())); pathsToDelete.removeAll(Arrays.asList(children)); for (String pathToDelete : pathsToDelete) { registry.delete(pathToDelete); filesystemManager.delete(RegistryUtils.getRelativePathToOriginal( pathToDelete, mountPath)); } } loadRegistryResources(registry, new File(filesystemManager.getBaseDir(), RegistryUtils.getRelativePathToOriginal(path, mountPath)), filesystemManager.getBaseDir().getAbsolutePath(), mountPath); } collection.setChildren(children); } else { return new OnDemandResourceImpl(filesystemManager, path, (ResourceImpl) resource); } requestContext.setProcessingComplete(true); return resource; } finally { CommonUtil.releaseSCMLock(); } } public void put(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isSCMLockAvailable()) { return; } validateUpdateInProgress(); String path = RegistryUtils.getRelativePathToOriginal( requestContext.getResourcePath().getPath(), mountPath); preparePut(path, requestContext); } private void preparePut(String path, RequestContext requestContext) throws RegistryException { Resource resource = requestContext.getResource(); if (resource instanceof Collection) { filesystemManager.createDirectory(path); } else { Object content = resource.getContent(); if (content instanceof String) { filesystemManager.createOrUpdateFile(path, RegistryUtils.encodeString(((String)content))); } else { filesystemManager.createOrUpdateFile(path, (byte[])content); } //resource.setContent(""); } } public void delete(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isSCMLockAvailable()) { return; } validateUpdateInProgress(); String path = RegistryUtils.getRelativePathToOriginal( requestContext.getResourcePath().getPath(), mountPath); filesystemManager.delete(path); } public void importResource(RequestContext requestContext) throws RegistryException { validateUpdateInProgress(); try { URL sourceURL = new URL(requestContext.getSourceURL()); InputStream inputStream = sourceURL.openStream(); try { requestContext.getResource().setContent(IOUtils.toByteArray(inputStream)); } finally { inputStream.close(); } } catch (MalformedURLException e) { throw new RegistryException("Unable to connect to URL", e); } catch (IOException e) { throw new RegistryException("Unable to download URL content", e); } put(requestContext); } public String move(RequestContext requestContext) throws RegistryException { validateUpdateInProgress(); String sourcePath = requestContext.getSourcePath(); String targetPath = requestContext.getTargetPath(); if (sourcePath.startsWith(mountPath) && targetPath.startsWith(mountPath)) { String source = RegistryUtils.getRelativePathToOriginal(sourcePath, mountPath); filesystemManager.copy(source, RegistryUtils.getRelativePathToOriginal(targetPath, mountPath)); filesystemManager.delete(source); } else if (targetPath.startsWith(mountPath)) { preparePut(RegistryUtils.getRelativePathToOriginal(targetPath, mountPath), requestContext); } else if (sourcePath.startsWith(mountPath)) { filesystemManager.delete( RegistryUtils.getRelativePathToOriginal(sourcePath, mountPath)); } return null; } public String copy(RequestContext requestContext) throws RegistryException { validateUpdateInProgress(); String sourcePath = requestContext.getSourcePath(); String targetPath = requestContext.getTargetPath(); if (sourcePath.startsWith(mountPath) && targetPath.startsWith(mountPath)) { filesystemManager.copy(RegistryUtils.getRelativePathToOriginal(sourcePath, mountPath), RegistryUtils.getRelativePathToOriginal(targetPath, mountPath)); } else if (targetPath.startsWith(mountPath)) { preparePut(RegistryUtils.getRelativePathToOriginal(targetPath, mountPath), requestContext); } return null; } public String rename(RequestContext requestContext) throws RegistryException { validateUpdateInProgress(); String sourcePath = requestContext.getSourcePath(); String targetPath = requestContext.getTargetPath(); String source = RegistryUtils.getRelativePathToOriginal(sourcePath, mountPath); filesystemManager.copy(source, RegistryUtils.getRelativePathToOriginal(targetPath, mountPath)); filesystemManager.delete(source); return null; } public void restore(RequestContext requestContext) throws RegistryException { log.warn("Skipping restoration to path: " + requestContext.getResourcePath().getPath()); } private static class OnDemandResourceImpl extends ResourceImpl { private String filePath; private FilesystemManager filesystemManager; public OnDemandResourceImpl(FilesystemManager filesystemManager, String filePath, ResourceImpl resource) { this.filePath = filePath; this.filesystemManager = filesystemManager; if (resource.getDescription() != null) { setDescription(resource.getDescription()); } if (resource.getMediaType() != null) { setMediaType(resource.getMediaType()); } if (resource.getProperties() != null) { setProperties(new Properties(resource.getProperties())); } setAuthorUserName(resource.getAuthorUserName()); setCreatedTime(new Date(resource.getCreatedTime().getTime())); setId(resource.getId()); setLastModified(new Date(resource.getLastModified().getTime())); setLastUpdaterUserName(resource.getLastUpdaterUserName()); setParentPath(resource.getParentPath()); setPath(resource.getPath()); setMatchingSnapshotID(resource.getMatchingSnapshotID()); } public Object getContent() throws RegistryException { try { if (content == null) { // fetch data only if it hasn't been fetched previously content = filesystemManager.getFileContent(filePath); } return content; } catch (Exception e) { throw new RegistryException("Failed to get resource content", e); } } } private void loadRegistryResources(Registry registry, File directory, String workingDir, String mountPoint) throws RegistryException { File[] files = directory.listFiles((FileFilter) new AndFileFilter(HiddenFileFilter.VISIBLE, new OrFileFilter(DirectoryFileFilter.INSTANCE, FileFileFilter.FILE))); if (files == null) { return; } for (File file : files) { if (file.isDirectory()) { loadRegistryResources(registry, file, workingDir, mountPoint); } else { // convert windows paths so that it fits into the Unix-like registry path structure. String path = mountPoint + file.getAbsolutePath().substring(workingDir.length()).replace("\\", "/"); if (!registry.resourceExists(path)) { registry.put(path, registry.newResource()); } } } } }