diff --git a/config/alfresco/alfresco-shared.properties b/config/alfresco/alfresco-shared.properties index 142fce6173..42502b77a7 100644 --- a/config/alfresco/alfresco-shared.properties +++ b/config/alfresco/alfresco-shared.properties @@ -14,3 +14,7 @@ avm.remote.host=localhost # AVMRemote API avm.remote.port=50500 +# Remote RMI services +rmi.services.remote.port=${avm.remote.port} +rmi.services.remote.host=${avm.remote.host} + diff --git a/config/alfresco/extension/remote-loader-context.xml b/config/alfresco/extension/remote-loader-context.xml new file mode 100644 index 0000000000..eb2a78757b --- /dev/null +++ b/config/alfresco/extension/remote-loader-context.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.service.cmr.remote.FileFolderRemote + + + org.alfresco.FileFolderRemote + + + ${rmi.services.remote.port} + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.service.cmr.remote.LoaderRemote + + + org.alfresco.LoaderRemote + + + ${rmi.services.remote.port} + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index 25983f85ce..df78c655c1 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -492,4 +492,21 @@ store.key.identifier = :storeIdentifier) + + select + count(node.id) + from + org.alfresco.repo.domain.hibernate.NodeImpl as node + + + + select + count(node.id) + from + org.alfresco.repo.domain.hibernate.NodeImpl as node + where + node.store.key.protocol = :protocol and + node.store.key.identifier = :identifier + + diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/AbstractLoaderThread.java b/source/java/org/alfresco/repo/model/filefolder/loader/AbstractLoaderThread.java new file mode 100644 index 0000000000..11cff034a5 --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/AbstractLoaderThread.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A description of what the remote loader should do. + * + * @author Derek Hulley + */ +public abstract class AbstractLoaderThread extends Thread +{ + protected final LoaderSession session; + protected final String loaderName; + protected final int testPeriod; + protected final int testTotal; + protected final int testLoadDepth; + + private AtomicBoolean mustStop; + + public AbstractLoaderThread( + LoaderSession session, + String loaderName, + int testPeriod, + int testTotal, + int testLoadDepth) + { + super(LoaderSession.THREAD_GROUP, "LoaderThread-" + loaderName); + + this.session = session; + this.loaderName = loaderName; + this.testPeriod = testPeriod; + this.testTotal = testTotal; + this.testLoadDepth = testLoadDepth; + + this.mustStop = new AtomicBoolean(false); + } + + /** + * Notify the running thread to exit at the first available opportunity. + */ + public void setStop() + { + mustStop.set(true); + } + + public abstract String getSummary(); + + @Override + public void run() + { + Random random = new Random(); + + int testCount = 0; + while (!mustStop.get()) + { + try + { + // Choose a server + int serverCount = session.getRemoteServers().size(); + int serverIndex = random.nextInt(serverCount); + LoaderServerProxy serverProxy = session.getRemoteServers().get(serverIndex); + + // Choose a working root node + int nodeCount = session.getWorkingRootNodeRefs().size(); + int nodeIndex = random.nextInt(nodeCount); + NodeRef workingRootNodeRef = session.getWorkingRootNodeRefs().get(nodeIndex); + + long startTime = System.currentTimeMillis(); + doLoading(serverProxy, workingRootNodeRef); + long endTime = System.currentTimeMillis(); + + // Have we done this enough? + testCount++; + if (testCount > testTotal) + { + break; + } + + // Do we wait or continue immediately + long duration = endTime - startTime; + long mustWait = (testPeriod * 1000L) - duration; + if (mustWait >= 5) + { + synchronized(this) + { + this.wait(mustWait); + } + } + } + catch (Throwable e) + { + session.logError(e.getMessage()); + } + } + } + + /** + * @param serverProxy the server to load + * @param workingRootNodeRef the root of the hierarchy to use + * @throws Exception any exception will be handled + */ + protected abstract void doLoading( + LoaderServerProxy serverProxy, + NodeRef workingRootNodeRef) throws Exception; +} diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java b/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java new file mode 100644 index 0000000000..fd6a3e0b8e --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * A class that communicates with the remote FileFolder interface to perform + * loading of the system using the standard models. + *

+ * TODO: The transport interface must be hidden behind and implementation + * of the standard FileFolderService interface. + * + * @author Derek Hulley + * @since 2.1 + */ +public class FileFolderRemoteLoader +{ + private static final String STORE_PROTOCOL = "FileFolderRemoteLoader"; + + private Properties properties; + private String username; + private String password; + + private LoaderSession session; + private AbstractLoaderThread[] threads; + + public FileFolderRemoteLoader(Properties properties, String username, String password) + { + this.properties = properties; + this.username = username; + this.password = password; + } + + public synchronized void initialize() throws Exception + { + if (session != null || threads != null) + { + throw new AlfrescoRuntimeException("Application already initialized"); + } + session = FileFolderRemoteLoader.makeSession(username, password, properties); + threads = FileFolderRemoteLoader.makeThreads(session, properties); + } + + public synchronized void start() + { + if (session == null || threads == null) + { + throw new AlfrescoRuntimeException("Application not initialized"); + } + // Fire up the threads + for (Thread thread : threads) + { + thread.start(); + } + } + + public synchronized void stop() + { + // Stop the threads + for (AbstractLoaderThread thread : threads) + { + thread.setStop(); + } + // Now join each thread to make sure they all finish their current operation + for (AbstractLoaderThread thread : threads) + { + try + { + thread.join(); + } + catch (InterruptedException e) {} + } + // Get each one's summary + for (AbstractLoaderThread thread : threads) + { + String summary = thread.getSummary(); + session.logNormal(summary); + } + } + + public static final String PROP_SESSION_NAME = "session.name"; + public static final String PROP_SESSION_VERBOSE = "session.verbose"; + public static final String PROP_SESSION_SOURCE_DIR = "session.sourceDir"; + public static final String PROP_SESSION_STORE_IDENTIFIERS = "session.storeIdentifiers"; + public static final String PROP_SESSION_RMI_URLS = "session.rmiUrls"; + public static final String PROP_SESSION_FOLDER_PROFILE = "session.folderProfile"; + + /** + * Factory method to construct a session using the given properties. + */ + private static LoaderSession makeSession(String username, String password, Properties properties) throws Exception + { + // Name + String name = properties.getProperty(PROP_SESSION_NAME); + FileFolderRemoteLoader.checkProperty(PROP_SESSION_STORE_IDENTIFIERS, name); + + // Verbose + String verboseStr = properties.getProperty(PROP_SESSION_VERBOSE); + boolean verbose = verboseStr == null ? false : Boolean.parseBoolean(verboseStr); + + // Source files + String sourceDirStr = properties.getProperty(PROP_SESSION_SOURCE_DIR); + File sourceDir = new File(sourceDirStr); + + // Stores + String storeIdentifiersStr = properties.getProperty(PROP_SESSION_STORE_IDENTIFIERS); + FileFolderRemoteLoader.checkProperty(PROP_SESSION_STORE_IDENTIFIERS, storeIdentifiersStr); + StringTokenizer tokenizer = new StringTokenizer(storeIdentifiersStr, ","); + Set storeRefs = new HashSet(); + while (tokenizer.hasMoreTokens()) + { + String storeIdentifier = tokenizer.nextToken().trim(); + storeRefs.add(new StoreRef(STORE_PROTOCOL, storeIdentifier)); + } + FileFolderRemoteLoader.checkProperty(PROP_SESSION_STORE_IDENTIFIERS, storeRefs); + + // RMI URLs + String rmiUrlsStr = properties.getProperty(PROP_SESSION_RMI_URLS); + FileFolderRemoteLoader.checkProperty(PROP_SESSION_RMI_URLS, rmiUrlsStr); + tokenizer = new StringTokenizer(rmiUrlsStr, ","); + Set rmiUrls = new HashSet(); + while (tokenizer.hasMoreTokens()) + { + String rmiUrl = tokenizer.nextToken().trim(); + rmiUrls.add(rmiUrl); + } + FileFolderRemoteLoader.checkProperty(PROP_SESSION_STORE_IDENTIFIERS, rmiUrls); + + // RMI URLs + String folderProfilesStr = properties.getProperty(PROP_SESSION_FOLDER_PROFILE); + FileFolderRemoteLoader.checkProperty(PROP_SESSION_FOLDER_PROFILE, folderProfilesStr); + tokenizer = new StringTokenizer(folderProfilesStr, ","); + ArrayList folderProfilesList = new ArrayList(5); + while (tokenizer.hasMoreTokens()) + { + String folderProfileStr = tokenizer.nextToken().trim(); + Integer folderProfile = Integer.valueOf(folderProfileStr); + folderProfilesList.add(folderProfile); + } + FileFolderRemoteLoader.checkProperty(PROP_SESSION_FOLDER_PROFILE, folderProfilesList); + int[] folderProfiles = new int[folderProfilesList.size()]; + for (int i = 0; i < folderProfiles.length; i++) + { + folderProfiles[i] = folderProfilesList.get(i); + } + + // Construct + LoaderSession session = new LoaderSession( + username, + password, + name, + rmiUrls, + storeRefs, + verbose, + sourceDir, + folderProfiles); + + // Initialize the session + session.initialize(); + + // Done + return session; + } + + /** + * Factory method to construct the worker threads. + */ + private static AbstractLoaderThread[] makeThreads(LoaderSession session, Properties properties) throws Exception + { + ArrayList threads = new ArrayList(3); + // Iterate over the properties and pick out the thread descriptors + for (String propertyName : properties.stringPropertyNames()) + { + if (!propertyName.startsWith("test.load.")) + { + continue; + } + String name = propertyName; + + // Get the type of the test + int lastIndex = propertyName.indexOf(".", 10); + if (lastIndex < 0) + { + throw new LoaderClientException( + "Invalid test loader property. " + + "It should be of the form 'test.load.upload.xyz=...': " + propertyName); + } + String type = propertyName.substring(10, lastIndex); + + // Get the values + String valuesStr = properties.getProperty(propertyName); + FileFolderRemoteLoader.checkProperty(propertyName, valuesStr); + // Parse it into the well-known values + int[] values = new int[] {1, 0, -1, 1}; + int index = 0; + StringTokenizer tokenizer = new StringTokenizer(valuesStr, ","); + while (tokenizer.hasMoreTokens()) + { + String value = tokenizer.nextToken().trim(); + values[index] = Integer.parseInt(value); + index++; + if (index >= values.length) + { + break; + } + } + int testCount = values[0]; + int testPeriod = values[1]; + int testTotal = values[2]; + int testDepth = values[3]; + + // Construct + for (int i = 0; i < testCount; i++) + { + AbstractLoaderThread thread = null; + if (type.equals("upload")) + { + thread = new LoaderUploadThread(session, name, testPeriod, testTotal, testDepth); + } + else + { + throw new LoaderClientException("Unknown test type: " + name); + } + threads.add(thread); + } + } + // Done + AbstractLoaderThread[] ret = new AbstractLoaderThread[threads.size()]; + return threads.toArray(ret); + } + + /** + * Checks for null values and empty collections. + */ + private static void checkProperty(String propertyName, Object propertyValue) throws LoaderClientException + { + if (propertyValue == null) + { + throw new LoaderClientException("'" + propertyName + "' must be provided."); + } + else if (propertyValue instanceof Collection) + { + Collection propertyCollection = (Collection) propertyValue; + int size = propertyCollection.size(); + if (size == 0) + { + throw new LoaderClientException("'" + propertyName + "' does not have any values."); + } + } + } + + public static void main(String ... args) + { + Map argMap = ripArgs(args); + String username = argMap.get("username"); + String password = argMap.get("password"); + String config = argMap.get("config"); + if (username == null || password == null || config == null) + { + printUsage(); + System.exit(1); + } + try + { + File propertiesFile = new File(config); + if (!propertiesFile.exists()) + { + System.err.println("Unable to find config file: " + config); + } + Properties properties = new Properties(); + properties.load(new FileInputStream(propertiesFile)); + + FileFolderRemoteLoader app = new FileFolderRemoteLoader(properties, username, password); + + // Initialize + app.initialize(); + + // Run + app.start(); + + synchronized(app) + { + app.wait(1000L); + } +// System.out.println("" + + // Finish off + app.stop(); + } + catch (LoaderClientException e) + { + System.err.println(e.getMessage()); + System.exit(1); + } + catch (Throwable e) + { + System.err.println("A failure prevented proper execution."); + e.printStackTrace(); + System.exit(1); + } + System.exit(0); + } + + private static Map ripArgs(String ... args) + { + Map argsMap = new HashMap(5); + for (String arg : args) + { + int index = arg.indexOf('='); + if (!arg.startsWith("--") || index < 0 || index == arg.length() - 1) + { + // Ignore it + continue; + } + String name = arg.substring(2, index); + String value = arg.substring(index + 1, arg.length()); + argsMap.put(name, value); + } + return argsMap; + } + + private static void printUsage() + { + StringBuilder sb = new StringBuilder(); + sb.append("\n") + .append("Usage\n") + .append(" java -jar ... --username= --password= --config= \n"); + System.out.println(sb.toString()); + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderClientException.java b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderClientException.java new file mode 100644 index 0000000000..932be5ab28 --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderClientException.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +/** + * Helper class to catch non-critical issues. + * + * @author Derek Hulley + */ +public class LoaderClientException extends Exception +{ + private static final long serialVersionUID = -3306363763048881956L; + + public LoaderClientException(String msg) + { + super(msg); + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderServerProxy.java b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderServerProxy.java new file mode 100644 index 0000000000..98fadb8a19 --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderServerProxy.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +import org.alfresco.service.cmr.remote.FileFolderRemote; +import org.alfresco.service.cmr.remote.LoaderRemote; + +/** + * Helper class to store remote service connections. + * + * @author Derek Hulley + */ +public class LoaderServerProxy +{ + public final String rmiUrl; + public final String ticket; + public final FileFolderRemote fileFolderRemote; + public final LoaderRemote loaderRemote; + + public LoaderServerProxy( + String rmiUrl, + String ticket, + FileFolderRemote fileFolderRemote, + LoaderRemote loaderRemote) + { + this.rmiUrl = rmiUrl; + this.ticket = ticket; + this.fileFolderRemote = fileFolderRemote; + this.loaderRemote = loaderRemote; + } +} + diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderSession.java b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderSession.java new file mode 100644 index 0000000000..7fd95c83dd --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderSession.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.remote.FileFolderRemoteClient; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.remote.LoaderRemote; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.remoting.rmi.RmiProxyFactoryBean; + +/** + * A description of session details. + * + * @author Derek Hulley + * @since 2.2 + */ +public class LoaderSession +{ + public static final ThreadGroup THREAD_GROUP = new ThreadGroup("FileFolderRemoteLoader"); + + private String username; + private String password; + private String name; + private Set rmiUrls; + private Set storeRefs; + private boolean verbose; + private File outputFile; + private File sourceDir; + private int[] folderProfiles; + + private List remoteServers; + private List workingRootNodeRefs; + private File[] sourceFiles; + private OutputStream outStream; + private OutputStream errStream; + + /** + * + */ + public LoaderSession( + String username, + String password, + String name, + Set rmiUrls, + Set storeRefs, + boolean verbose, + File sourceDir, + int[] folderProfiles) + { + this.username = username; + this.password = password; + this.name = name; + this.rmiUrls = rmiUrls; + this.storeRefs = storeRefs; + this.verbose = verbose; + this.sourceDir = sourceDir; + this.folderProfiles = folderProfiles; + } + + public String getName() + { + return name; + } + + public List getRemoteServers() + { + return remoteServers; + } + + public List getWorkingRootNodeRefs() + { + return workingRootNodeRefs; + } + + public File[] getSourceFiles() + { + return sourceFiles; + } + + public int[] getFolderProfiles() + { + return folderProfiles; + } + + /** + * Initialize the object before first use. + */ + public synchronized void initialize() throws Exception + { + if (remoteServers != null) + { + throw new AlfrescoRuntimeException("The client has already been initialized"); + } + remoteServers = LoaderSession.connect(rmiUrls, username, password); + workingRootNodeRefs = LoaderSession.makeStores(remoteServers, storeRefs); + LoaderSession.checkClustering(remoteServers, workingRootNodeRefs); + + // Create the output file, if necessary + if (outputFile != null) + { + File outputDir = outputFile.getParentFile(); + if (!outputDir.exists()) + { + outputDir.mkdirs(); + } + if (outputFile.exists()) + { + System.out.println("The output file " + outputFile + " already exists."); + System.out.println("Are you sure you want to overwrite the file?"); + int in = System.in.read(); + if (in != 'Y' && in != 'y') + { + throw new LoaderClientException("The output file " + outputFile + " already exists"); + } + } + } + + // Get the source files + sourceFiles = LoaderSession.getSourceFiles(sourceDir); + + // Construct output and error files + long time = System.currentTimeMillis(); + File outFile = new File("./LoaderSession-" + name + "-"+ time + "-out.txt"); + File errFile = new File("./LoaderSession-" + name + "-"+ time + "-err.txt"); + outStream = new BufferedOutputStream(new FileOutputStream(outFile)); + errStream = new BufferedOutputStream(new FileOutputStream(errFile)); + } + + public synchronized void close() + { + try { outStream.close(); } catch (Throwable e) {} + try { errStream.close(); } catch (Throwable e) {} + + outStream = null; + errStream = null; + } + + /** + * Connect to the remote servers. + */ + private static List connect(Set rmiUrls, String username, String password) throws Exception + { + List remoteServers = new ArrayList(rmiUrls.size()); + for (String rmiUrl : rmiUrls) + { + try + { + // Ensure the RMI URL is consistent + if (!rmiUrl.endsWith("/")) + { + rmiUrl += "/"; + } + // Get the FileFolderServiceTransport + FileFolderRemoteClient fileFolderRemote = new FileFolderRemoteClient(rmiUrl); + // Get the LoaderServiceTransport + RmiProxyFactoryBean loaderFactory = new RmiProxyFactoryBean(); + loaderFactory.setRefreshStubOnConnectFailure(true); + loaderFactory.setServiceInterface(LoaderRemote.class); + loaderFactory.setServiceUrl(rmiUrl + LoaderRemote.SERVICE_NAME); + loaderFactory.afterPropertiesSet(); + LoaderRemote loaderRemote = (LoaderRemote) loaderFactory.getObject(); + + // Authenticate + String ticket = loaderRemote.authenticate(username, password); + + // Store the service references + LoaderServerProxy remoteServer = new LoaderServerProxy( + rmiUrl, + ticket, + fileFolderRemote, + loaderRemote); + remoteServers.add(remoteServer); + } + catch (Throwable e) + { + // Failed to connect. + System.err.println("\n" + + "ERROR: Failed to establish connection to server: \n" + + " Server: " + rmiUrl + "\n" + + " Error: " + e.getMessage()); + e.printStackTrace(); + } + } + // Check that there is at least one server + if (remoteServers.size() == 0) + { + throw new LoaderClientException("No remote servers are available"); + } + return remoteServers; + } + + private static List makeStores(List remoteServers, Set storeRefs) throws Exception + { + // Take the first server and ensure that all the stores are available + LoaderServerProxy remoteServer = remoteServers.get(0); + + List workingRootNodeRefs = new ArrayList(10); + for (StoreRef storeRef : storeRefs) + { + NodeRef workingRootNodeRef = remoteServer.loaderRemote.getOrCreateWorkingRoot(remoteServer.ticket, storeRef); + workingRootNodeRefs.add(workingRootNodeRef); + } + // Done + return workingRootNodeRefs; + } + + private static void checkClustering( + List remoteServers, + List workingRootNodeRefs) throws Exception + { + List problems = new ArrayList(10); + for (LoaderServerProxy remoteServer : remoteServers) + { + String ticket = remoteServer.ticket; + for (NodeRef workingRootNodeRef : workingRootNodeRefs) + { + try + { + // Get the working root node. If the cluster is correctly configured, + // it will be available on all the servers. + FileInfo fileInfo = remoteServer.fileFolderRemote.getFileInfo(ticket, workingRootNodeRef); + if (fileInfo == null) + { + problems.add("Cannot find the working root node on server: " + remoteServer.rmiUrl); + continue; + } + // Now look for and create a file against which to store some content + String sampleFilename = "InitialSample.txt"; + String sampleContent = "Sample content"; + NodeRef sampleNodeRef = remoteServer.fileFolderRemote.searchSimple(ticket, workingRootNodeRef, sampleFilename); + if (sampleNodeRef == null) + { + FileInfo sampleFileInfo = remoteServer.fileFolderRemote.create( + ticket, workingRootNodeRef, sampleFilename, ContentModel.TYPE_CONTENT); + sampleNodeRef = sampleFileInfo.getNodeRef(); + // Write some content + byte[] bytes = sampleContent.getBytes("UTF-8"); + remoteServer.fileFolderRemote.putContent(ticket, sampleNodeRef, bytes, sampleFilename); + } + // Get the content and ensure that it is the same + byte[] bytes = remoteServer.fileFolderRemote.getContent(ticket, sampleNodeRef); + if (bytes == null) + { + problems.add("Sample content was not found on server: " + remoteServer.rmiUrl); + continue; + } + String checkContent = new String(bytes, "UTF-8"); + if (!checkContent.equals(sampleContent)) + { + problems.add("The sample content differed from expected: " + remoteServer.rmiUrl); + continue; + } + } + catch (Throwable e) + { + System.err.println("ERROR: Failure whilst checking server: " + remoteServer.rmiUrl); + e.printStackTrace(); + problems.add(e.getMessage()); + } + } + } + // Check for issues + if (problems.size() > 0) + { + StringBuilder sb = new StringBuilder(); + sb.append("\n") + .append("The working root node references could not be found on all the remote servers.\n") + .append("Please ensure that all the remote servers listed are active in a single cluster."); + for (String problem : problems) + { + sb.append("\n") + .append(" ").append(problem); + } + throw new LoaderClientException(sb.toString()); + } + } + + private static File[] getSourceFiles(File sourceDir) throws Exception + { + // Ensure that the source directory is present, if specified + if (sourceDir != null) + { + if (!sourceDir.exists()) + { + throw new LoaderClientException("The source directory to contain upload files is missing: " + sourceDir); + } + // Check that there is something in it + File[] allFiles = sourceDir.listFiles(); + ArrayList sourceFiles = new ArrayList(allFiles.length); + for (File file : allFiles) + { + if (file.isDirectory()) + { + continue; + } + sourceFiles.add(file); + } + File[] ret = new File[sourceFiles.size()]; + return sourceFiles.toArray(ret); + } + else + { + return new File[] {}; + } + } + + public void logVerbose(String msg) + { + if (!verbose) + { + // Just ignore it + } + logNormal(msg); + } + + public synchronized void logNormal(String msg) + { + if (outStream == null) + { + return; + } + try + { + byte[] bytes = msg.getBytes("UTF-8"); + outStream.write(bytes); + outStream.write('\n'); + outStream.flush(); + } + catch (Throwable e) + { + System.err.println("Failed to write message to output file: " + e.getMessage()); + } + } + + public synchronized void logError(String msg) + { + if (errStream == null) + { + return; + } + try + { + byte[] bytes = msg.getBytes("UTF-8"); + errStream.write(bytes); + outStream.write('\n'); + errStream.flush(); + } + catch (Throwable e) + { + System.err.println("Failed to write message to output file: " + e.getMessage()); + } + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java new file mode 100644 index 0000000000..0cd6afc41a --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder.loader; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A description of what the remote loader should do. + * + * @author Derek Hulley + */ +public class LoaderUploadThread extends AbstractLoaderThread +{ + public LoaderUploadThread( + LoaderSession session, + String loaderName, + int testPeriod, + int testTotal, + int testLoadDepth) + { + super(session, loaderName, testPeriod, testTotal, testLoadDepth); + } + + @Override + protected void doLoading(LoaderServerProxy serverProxy, NodeRef workingRootNodeRef) throws Exception + { + int totalNodeCount = serverProxy.loaderRemote.getNodeCount( + serverProxy.ticket); + int storeNodeCount = serverProxy.loaderRemote.getNodeCount( + serverProxy.ticket, + workingRootNodeRef.getStoreRef()); + session.logVerbose("Nodes: " + totalNodeCount + ". Store Nodes: " + storeNodeCount); + } + + @Override + public String getSummary() + { + return "Tanker"; + } +} diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index ba9a7aa214..2523ebadbd 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -284,6 +284,15 @@ public interface NodeDaoService */ public List getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition); + /** + * @return Returns the total number of nodes in the ADM repository + */ + public int getNodeCount(); + /** + * @return Returns the total number of nodes in the ADM store + */ + public int getNodeCount(final StoreRef storeRef); + public Transaction getTxnById(long txnId); public Transaction getLastTxn(); public Transaction getLastRemoteTxn(); diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index 70a79a47e0..31c943a4b6 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -105,6 +105,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs"; private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_ACTUAL_TYPE = "node.GetNodesWithPropertyValuesByActualType"; private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; + + private static final String QUERY_GET_NODE_COUNT = "node.GetNodeCount"; + private static final String QUERY_GET_NODE_COUNT_FOR_STORE = "node.GetNodeCountForStore"; private static final String QUERY_GET_NODE_STATUSES_FOR_STORE = "node.GetNodeStatusesForStore"; private static final String QUERY_GET_CHILD_ASSOCS_FOR_STORE = "node.GetChildAssocsForStore"; @@ -367,6 +370,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements * @param protocol the store protocol * @param identifier the store identifier */ + @SuppressWarnings("unchecked") public void deleteStore(final String protocol, final String identifier) { // ensure that the store exists @@ -1368,6 +1372,48 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements return convertedValues; } + /** + * {@inheritDoc} + */ + public int getNodeCount() + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(QUERY_GET_NODE_COUNT); + query.setMaxResults(1) + .setReadOnly(true); + return query.uniqueResult(); + } + }; + Long count = (Long) getHibernateTemplate().execute(callback); + // done + return count.intValue(); + } + + /** + * {@inheritDoc} + */ + public int getNodeCount(final StoreRef storeRef) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(QUERY_GET_NODE_COUNT_FOR_STORE); + query.setString("protocol", storeRef.getProtocol()) + .setString("identifier", storeRef.getIdentifier()) + .setMaxResults(1) + .setReadOnly(true); + return query.uniqueResult(); + } + }; + Long count = (Long) getHibernateTemplate().execute(callback); + // done + return count.intValue(); + } + /* * Queries for transactions */ diff --git a/source/java/org/alfresco/repo/remote/FileFolderRemoteClient.java b/source/java/org/alfresco/repo/remote/FileFolderRemoteClient.java new file mode 100644 index 0000000000..a1de8781a0 --- /dev/null +++ b/source/java/org/alfresco/repo/remote/FileFolderRemoteClient.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.remote; + +import java.util.List; + +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.remote.FileFolderRemote; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.springframework.remoting.rmi.RmiProxyFactoryBean; + +/** + * Client side implementation of the remotable FileFolder interface. + * + * @author Derek Hulley + * @since 2.2 + */ +public class FileFolderRemoteClient implements FileFolderRemote +{ + private String rmiUrl; + private FileFolderRemote remotePeer; + + public FileFolderRemoteClient(String rmiUrl) + { + // Ensure the RMI URL is consistent + if (!rmiUrl.endsWith("/")) + { + rmiUrl += "/"; + } + + this.rmiUrl = rmiUrl; + // Connect + connect(); + } + + private void connect() + { + // Get the FileFolderServiceTransport + RmiProxyFactoryBean fileFolderFactory = new RmiProxyFactoryBean(); + fileFolderFactory.setRefreshStubOnConnectFailure(true); + fileFolderFactory.setServiceInterface(FileFolderRemote.class); + fileFolderFactory.setServiceUrl(rmiUrl + FileFolderRemote.SERVICE_NAME); + fileFolderFactory.afterPropertiesSet(); + FileFolderRemote fileFolderRemote = (FileFolderRemote) fileFolderFactory.getObject(); + this.remotePeer = fileFolderRemote; + } + + /** + * {@inheritDoc} + */ + public List list(String ticket, final NodeRef contextNodeRef) + { + return remotePeer.list(ticket, contextNodeRef); + } + + /** + * {@inheritDoc} + */ + public List listFiles(String ticket, final NodeRef folderNodeRef) + { + return remotePeer.listFiles(ticket, folderNodeRef); + } + + /** + * {@inheritDoc} + */ + public List listFolders(String ticket, final NodeRef contextNodeRef) + { + return remotePeer.listFolders(ticket, contextNodeRef); + } + + /** + * {@inheritDoc} + */ + public NodeRef searchSimple(String ticket, final NodeRef contextNodeRef, final String name) + { + return remotePeer.searchSimple(ticket, contextNodeRef, name); + } + + /** + * {@inheritDoc} + */ + public List search( + String ticket, + final NodeRef contextNodeRef, + final String namePattern, + final boolean includeSubFolders) + { + return remotePeer.search(ticket, contextNodeRef, namePattern, includeSubFolders); + } + + /** + * {@inheritDoc} + */ + public List search( + String ticket, + final NodeRef contextNodeRef, + final String namePattern, + final boolean fileSearch, + final boolean folderSearch, + final boolean includeSubFolders) + { + return remotePeer.search(ticket, contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders); + } + + /** + * {@inheritDoc} + */ + public FileInfo rename(String ticket, final NodeRef fileFolderRef, final String newName) throws FileExistsException, FileNotFoundException + { + return remotePeer.rename(ticket, fileFolderRef, newName); + } + + /** + * {@inheritDoc} + */ + public FileInfo move(String ticket, final NodeRef sourceNodeRef, final NodeRef targetParentRef, final String newName) + throws FileExistsException, FileNotFoundException + { + return remotePeer.move(ticket, sourceNodeRef, targetParentRef, newName); + } + + /** + * {@inheritDoc} + */ + public FileInfo copy(String ticket, final NodeRef sourceNodeRef, final NodeRef targetParentRef, final String newName) + throws FileExistsException, FileNotFoundException + { + return remotePeer.copy(ticket, sourceNodeRef, targetParentRef, newName); + } + + /** + * {@inheritDoc} + */ + public FileInfo create(String ticket, final NodeRef parentNodeRef, final String name, final QName typeQName) throws FileExistsException + { + return remotePeer.create(ticket, parentNodeRef, name, typeQName); + } + + /** + * {@inheritDoc} + */ + public void delete(String ticket, final NodeRef nodeRef) + { + remotePeer.delete(ticket, nodeRef); + } + + /** + * {@inheritDoc} + */ + public FileInfo makeFolders(String ticket, final NodeRef parentNodeRef, final List pathElements, final QName folderTypeQName) + { + return remotePeer.makeFolders(ticket, parentNodeRef, pathElements, folderTypeQName); + } + + /** + * {@inheritDoc} + */ + public List getNamePath(String ticket, final NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException + { + return remotePeer.getNamePath(ticket, rootNodeRef, nodeRef); + } + + /** + * {@inheritDoc} + */ + public FileInfo resolveNamePath(String ticket, final NodeRef rootNodeRef, final List pathElements) throws FileNotFoundException + { + return remotePeer.resolveNamePath(ticket, rootNodeRef, pathElements); + } + + /** + * {@inheritDoc} + */ + public FileInfo getFileInfo(String ticket, final NodeRef nodeRef) + { + return remotePeer.getFileInfo(ticket, nodeRef); + } + + /** + * {@inheritDoc} + */ + public ContentData putContent(String ticket, NodeRef nodeRef, byte[] bytes, String filename) + { + return remotePeer.putContent(ticket, nodeRef, bytes, filename); + } + + /** + * {@inheritDoc} + */ + public byte[] getContent(String ticket, NodeRef nodeRef) + { + return remotePeer.getContent(ticket, nodeRef); + } + + /** + * {@inheritDoc} + */ + public ContentReader getReader(String ticket, NodeRef nodeRef) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + */ + public ContentWriter getWriter(String ticket, NodeRef nodeRef) + { + throw new UnsupportedOperationException(); + } +} diff --git a/source/java/org/alfresco/repo/remote/FileFolderRemoteServer.java b/source/java/org/alfresco/repo/remote/FileFolderRemoteServer.java new file mode 100644 index 0000000000..c4db4dcc2e --- /dev/null +++ b/source/java/org/alfresco/repo/remote/FileFolderRemoteServer.java @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.remote; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; +import java.util.List; + +import net.sf.acegisecurity.Authentication; + +import org.alfresco.repo.content.encoding.ContentCharsetFinder; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.remote.FileFolderRemote; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; + +/** + * Server side implementation of the FileFolderService transport + * layer. This is the class that gets exported remotely as it contains the + * explicit ticket arguments. + * + * @author Derek Hulley + * @since 2.2 + */ +public class FileFolderRemoteServer implements FileFolderRemote +{ + private RetryingTransactionHelper retryingTransactionHelper; + private AuthenticationService authenticationService; + private FileFolderService fileFolderService; + private MimetypeService mimetypeService; + + /** + * @param transactionService provides transactional support and retrying + */ + public void setTransactionService(TransactionService transactionService) + { + this.retryingTransactionHelper = transactionService.getRetryingTransactionHelper(); + } + + /** + * @param authenticationService the service that will validate the tickets + */ + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + /** + * @param filefolderService the service that will do the work + */ + public void setFileFolderService(FileFolderService filefolderService) + { + this.fileFolderService = filefolderService; + } + + /** + * @param mimetypeService used to determine the character encoding + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + /** + * {@inheritDoc} + */ + public List list(String ticket, final NodeRef contextNodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.list(contextNodeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public List listFiles(String ticket, final NodeRef folderNodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.listFiles(folderNodeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public List listFolders(String ticket, final NodeRef contextNodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.listFolders(contextNodeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public NodeRef searchSimple(String ticket, final NodeRef contextNodeRef, final String name) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return fileFolderService.searchSimple(contextNodeRef, name); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public List search( + String ticket, + final NodeRef contextNodeRef, + final String namePattern, + final boolean includeSubFolders) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.search(contextNodeRef, namePattern, includeSubFolders); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public List search( + String ticket, + final NodeRef contextNodeRef, + final String namePattern, + final boolean fileSearch, + final boolean folderSearch, + final boolean includeSubFolders) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.search(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo rename(String ticket, final NodeRef fileFolderRef, final String newName) throws FileExistsException, FileNotFoundException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.rename(fileFolderRef, newName); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo move(String ticket, final NodeRef sourceNodeRef, final NodeRef targetParentRef, final String newName) + throws FileExistsException, FileNotFoundException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.move(sourceNodeRef, targetParentRef, newName); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo copy(String ticket, final NodeRef sourceNodeRef, final NodeRef targetParentRef, final String newName) + throws FileExistsException, FileNotFoundException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.copy(sourceNodeRef, targetParentRef, newName); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo create(String ticket, final NodeRef parentNodeRef, final String name, final QName typeQName) throws FileExistsException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.create(parentNodeRef, name, typeQName); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public void delete(String ticket, final NodeRef nodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + fileFolderService.delete(nodeRef); + return null; + } + }; + retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo makeFolders(String ticket, final NodeRef parentNodeRef, final List pathElements, final QName folderTypeQName) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.makeFolders(parentNodeRef, pathElements, folderTypeQName); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public List getNamePath(String ticket, final NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + return fileFolderService.getNamePath(rootNodeRef, nodeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo resolveNamePath(String ticket, final NodeRef rootNodeRef, final List pathElements) throws FileNotFoundException + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.resolveNamePath(rootNodeRef, pathElements); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public FileInfo getFileInfo(String ticket, final NodeRef nodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo execute() throws Throwable + { + return fileFolderService.getFileInfo(nodeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, true, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public ContentData putContent( + String ticket, + final NodeRef nodeRef, + final byte[] bytes, + final String filename) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public ContentData execute() throws Throwable + { + // Guess the mimetype + String mimetype = mimetypeService.guessMimetype(filename); + + // Get a writer + ContentWriter writer = fileFolderService.getWriter(nodeRef); + // Make a stream + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + // Guess the encoding + ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder(); + Charset charset = charsetFinder.getCharset(is, mimetype); + // Set metadata + writer.setEncoding(charset.name()); + writer.setMimetype(mimetype); + + // Write the stream + writer.putContent(is); + + // Done + return writer.getContentData(); + } + }; + ContentData contentData = retryingTransactionHelper.doInTransaction(callback, false, true); + // Done + return contentData; + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public byte[] getContent(String ticket, final NodeRef nodeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public byte[] execute() throws Throwable + { + // Get a reader + ContentReader reader = fileFolderService.getReader(nodeRef); + if (!reader.exists()) + { + return null; + } + + // Extract the content + ByteArrayOutputStream bos = new ByteArrayOutputStream((int)reader.getSize()); + reader.getContent(bos); + + // Done + return bos.toByteArray(); + } + }; + byte[] bytes = retryingTransactionHelper.doInTransaction(callback, true, true); + // Done + return bytes; + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + + /** + * {@inheritDoc} + */ + public ContentReader getReader(String ticket, NodeRef nodeRef) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + */ + public ContentWriter getWriter(String ticket, NodeRef nodeRef) + { + throw new UnsupportedOperationException(); + } +} diff --git a/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java b/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java new file mode 100644 index 0000000000..24e2faae2a --- /dev/null +++ b/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.remote; + +import java.util.List; + +import net.sf.acegisecurity.Authentication; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.db.NodeDaoService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.remote.LoaderRemote; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Server side implementation of the LoaderServiceTransport transport + * layer. This is the class that gets exported remotely as it contains the + * explicit ticket arguments. + * + * @author Derek Hulley + * @since 2.2 + */ +public class LoaderRemoteServer implements LoaderRemote +{ + private static Log logger = LogFactory.getLog(LoaderRemoteServer.class); + + /** The association for the working root node: sys:LoaderServiceWorkingRoot */ + private static final QName ASSOC_WORKING_ROOT = QName.createQName( + NamespaceService.SYSTEM_MODEL_1_0_URI, + "LoaderServiceWorkingRoot"); + + private RetryingTransactionHelper retryingTransactionHelper; + private AuthenticationService authenticationService; + private NodeService nodeService; + private NodeDaoService nodeDaoService; + + /** + * @param transactionService provides transactional support and retrying + */ + public void setTransactionService(TransactionService transactionService) + { + this.retryingTransactionHelper = transactionService.getRetryingTransactionHelper(); + } + + /** + * @param authenticationService the service that will validate the tickets + */ + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + /** + * @param nodeService the service that will do the work + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param nodeDaoService the DAO for node queries + */ + public void setNodeDaoService(NodeDaoService nodeDaoService) + { + this.nodeDaoService = nodeDaoService; + } + + /** + * {@inheritDoc} + */ + public String authenticate(String username, String password) + { + // We need to authenticate + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.authenticate(username, password.toCharArray()); + String ticket = authenticationService.getCurrentTicket(); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Authenticated: " + username); + } + return ticket; + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public NodeRef getOrCreateWorkingRoot(String ticket, final StoreRef storeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef rootNodeRef = null; + // Check if the store exists + if (!nodeService.exists(storeRef)) + { + nodeService.createStore(storeRef.getProtocol(), storeRef.getIdentifier()); + } + rootNodeRef = nodeService.getRootNode(storeRef); + // Look for the working root + List assocRefs = nodeService.getChildAssocs( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + ASSOC_WORKING_ROOT); + NodeRef workingRootNodeRef = null; + if (assocRefs.size() > 0) + { + // Just use the first one + workingRootNodeRef = assocRefs.get(0).getChildRef(); + } + else + { + String username = authenticationService.getCurrentUserName(); + // We have to make it. Touch the root node to act as an optimistic lock. + nodeService.setProperty(rootNodeRef, ContentModel.PROP_AUTHOR, username); + // Add a cm:folder below the root + PropertyMap properties = new PropertyMap(); + properties.put(ContentModel.PROP_NAME, "Loader Application Root"); + workingRootNodeRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + ASSOC_WORKING_ROOT, + ContentModel.TYPE_FOLDER, + properties).getChildRef(); + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Got working root node: " + workingRootNodeRef); + } + return workingRootNodeRef; + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public int getNodeCount(String ticket) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + return nodeDaoService.getNodeCount(); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } + + /** + * {@inheritDoc} + */ + public int getNodeCount(String ticket, final StoreRef storeRef) + { + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + return nodeDaoService.getNodeCount(storeRef); + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } +} diff --git a/source/java/org/alfresco/service/cmr/remote/FileFolderRemote.java b/source/java/org/alfresco/service/cmr/remote/FileFolderRemote.java new file mode 100644 index 0000000000..55ff569e2d --- /dev/null +++ b/source/java/org/alfresco/service/cmr/remote/FileFolderRemote.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.remote; + +import java.util.List; + +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Remote transport interface for the FileFolderService. This includes the + * authentication tickets and abstracts the stream transport as well. + *

+ * NOTE: This is not a production API and will most definitely be changed or removed. + * + * @see org.alfresco.service.cmr.model.FileFolderService + * + * @author Derek Hulley + * @since 2.2. + */ +public interface FileFolderRemote +{ + /** The service name org.alfresco.FileFolderRemote */ + public static final String SERVICE_NAME = "org.alfresco.FileFolderRemote"; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#list(NodeRef) + */ + public List list(String ticket, NodeRef contextNodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#listFiles(NodeRef) + */ + public List listFiles(String ticket, NodeRef folderNodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#listFolders(NodeRef) + */ + public List listFolders(String ticket, NodeRef contextNodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#searchSimple(NodeRef, String) + */ + public NodeRef searchSimple(String ticket, NodeRef contextNodeRef, String name); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#search(NodeRef, String, boolean) + */ + public List search( + String ticket, + NodeRef contextNodeRef, + String namePattern, + boolean includeSubFolders); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#search(NodeRef, String, boolean, boolean, boolean) + */ + public List search( + String ticket, + NodeRef contextNodeRef, + String namePattern, + boolean fileSearch, + boolean folderSearch, + boolean includeSubFolders); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#rename(NodeRef, String) + */ + public FileInfo rename(String ticket, NodeRef fileFolderRef, String newName) throws FileExistsException, FileNotFoundException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#move(NodeRef, NodeRef, String) + */ + public FileInfo move(String ticket, NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) + throws FileExistsException, FileNotFoundException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#copy(NodeRef, NodeRef, String) + */ + public FileInfo copy(String ticket, NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) + throws FileExistsException, FileNotFoundException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#create(NodeRef, String, QName) + */ + public FileInfo create(String ticket, NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#delete(NodeRef) + */ + public void delete(String ticket, NodeRef nodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#makeFolders(NodeRef, List, QName) + */ + public FileInfo makeFolders(String ticket, NodeRef parentNodeRef, List pathElements, QName folderTypeQName); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#getNamePath(NodeRef, NodeRef) + */ + public List getNamePath(String ticket, NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#resolveNamePath(NodeRef, List) + */ + public FileInfo resolveNamePath(String ticket, NodeRef rootNodeRef, List pathElements) throws FileNotFoundException; + + /** + * @param token the authentication ticket + * + * @see FileFolderService#getFileInfo(NodeRef) + */ + public FileInfo getFileInfo(String ticket, NodeRef nodeRef); + + /** + * TODO: Refactor!!! + * The dirtiest of hacks. When time permits, the APIs and implementations will be properly refactored. + * For now, this remains adequate for small files. + */ + public ContentData putContent(String ticket, NodeRef nodeRef, byte[] bytes, String filename); + + /** + * TODO: Refactor!!! + */ + public byte[] getContent(String ticket, NodeRef nodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#getReader(NodeRef) + */ + public ContentReader getReader(String ticket, NodeRef nodeRef); + + /** + * @param token the authentication ticket + * + * @see FileFolderService#getWriter(NodeRef) + */ + public ContentWriter getWriter(String ticket, NodeRef nodeRef); +} diff --git a/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java b/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java new file mode 100644 index 0000000000..fc1ffe9777 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.remote; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * Remote transport interface for the Loader application. This adds functionality + * that is generally required by the application and that is not available on other + * interfaces. + * + * @see FileFolderRemote + * + * @author Derek Hulley + * @since 2.2. + */ +public interface LoaderRemote +{ + /** The service name org.alfresco.LoaderRemote */ + public static final String SERVICE_NAME = "org.alfresco.LoaderRemote"; + + /** + * Authenticate on the server using the given username and password + * + * @return the authentication ticket + */ + public String authenticate(String username, String password); + + /** + * Get the working cm:folder node for the given store. If there is no working + * root node, then one is created. + * + * @param ticket the authentication ticket + * @param storeRef the store reference + * @return a working cm:folder to use as the root for loading, + * or null if it is not available. + */ + public NodeRef getOrCreateWorkingRoot(String ticket, StoreRef storeRef); + + /** + * @param ticket the authentication ticket + * @return Returns the total number of ADM nodes + */ + public int getNodeCount(String ticket); + + /** + * @param ticket the authentication ticket + * @param storeRef the store to query against + * @return Returns the total number of nodes for the given ADM store + */ + public int getNodeCount(String ticket, StoreRef storeRef); +}