From a65d2952fcff511ee5e57e6cfda3802ed56ba748 Mon Sep 17 00:00:00 2001 From: Gary Spencer Date: Mon, 20 Nov 2006 15:24:42 +0000 Subject: [PATCH] Added the AVM share mapper to allow mapping read-only shares to any version of a store. Added the configuration tag to add shares for all stores, connected to the head version of the store. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4397 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org/alfresco/filesys/avm/AVMContext.java | 10 + .../alfresco/filesys/avm/AVMDiskDriver.java | 79 +++- .../alfresco/filesys/avm/AVMShareMapper.java | 347 ++++++++++++++++++ .../server/config/ServerConfiguration.java | 74 ++++ 4 files changed, 499 insertions(+), 11 deletions(-) create mode 100644 source/java/org/alfresco/filesys/avm/AVMShareMapper.java diff --git a/source/java/org/alfresco/filesys/avm/AVMContext.java b/source/java/org/alfresco/filesys/avm/AVMContext.java index 4e74373fe9..33b199e11a 100644 --- a/source/java/org/alfresco/filesys/avm/AVMContext.java +++ b/source/java/org/alfresco/filesys/avm/AVMContext.java @@ -19,6 +19,7 @@ package org.alfresco.filesys.avm; import org.alfresco.filesys.server.filesys.DiskDeviceContext; import org.alfresco.filesys.server.filesys.FileSystem; +import org.alfresco.filesys.server.filesys.SrvDiskInfo; /** * AVM Filesystem Context Class @@ -59,6 +60,15 @@ public class AVMContext extends DiskDeviceContext { // Set the store version to use m_version = version; + + // Default the filesystem to look like an 80Gb sized disk with 90% free space + + setDiskInformation(new SrvDiskInfo(2560000, 64, 512, 2304000)); + + // Set filesystem parameters + + setFilesystemAttributes(FileSystem.CasePreservedNames + FileSystem.UnicodeOnDisk + + FileSystem.CaseSensitiveSearch); } /** diff --git a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java index 87fcaf5d2c..26b6db8f00 100644 --- a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java +++ b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java @@ -19,6 +19,7 @@ package org.alfresco.filesys.avm; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.List; import java.util.SortedMap; import javax.transaction.UserTransaction; @@ -36,11 +37,10 @@ import org.alfresco.filesys.server.filesys.FileInfo; import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.FileOpenParams; import org.alfresco.filesys.server.filesys.FileStatus; -import org.alfresco.filesys.server.filesys.FileSystem; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.SearchContext; -import org.alfresco.filesys.server.filesys.SrvDiskInfo; import org.alfresco.filesys.server.filesys.TreeConnection; +import org.alfresco.filesys.util.StringList; import org.alfresco.filesys.util.WildCard; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; @@ -48,6 +48,7 @@ import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMNotFoundException; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.avm.AVMWrongTypeException; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.security.AuthenticationService; @@ -315,15 +316,6 @@ public class AVMDiskDriver implements DiskInterface { // Create the context context = new AVMContext(storePath, version); - - // Default the filesystem to look like an 80Gb sized disk with 90% free space - - context.setDiskInformation(new SrvDiskInfo(2560000, 64, 512, 2304000)); - - // Set parameters - - context.setFilesystemAttributes(FileSystem.CasePreservedNames + FileSystem.UnicodeOnDisk + - FileSystem.CaseSensitiveSearch); } catch (Exception ex) { @@ -355,6 +347,71 @@ public class AVMDiskDriver implements DiskInterface { return context; } + /** + * Return a list of the available AVM store names + * + * @return StringList + */ + public final StringList getAVMStoreNames() + { + // Use the system user as the authenticated context to get the AVM store list + + m_authComponent.setCurrentUser( m_authComponent.getSystemUserName()); + + // Wrap the service request in a transaction + + UserTransaction tx = m_transactionService.getUserTransaction(false); + + StringList storeNames = new StringList(); + + try + { + // Start the transaction + + if ( tx != null) + tx.begin(); + + // Get the list of AVM stores + + List storeList = m_avmService.getAVMStores(); + + if ( storeList != null) + { + for ( AVMStoreDescriptor storeDesc : storeList) + storeNames.addString( storeDesc.getName()); + } + + // Commit the transaction + + tx.commit(); + tx = null; + } + catch (Exception ex) + { + logger.error("Error during create context", ex); + } + finally + { + // If there is an active transaction then roll it back + + if ( tx != null) + { + try + { + tx.rollback(); + } + catch (Exception ex) + { + logger.warn("Failed to rollback transaction", ex); + } + } + } + + // Return the list of AVM store names + + return storeNames; + } + /** * Build the full store path for a file/folder using the share relative path * diff --git a/source/java/org/alfresco/filesys/avm/AVMShareMapper.java b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java new file mode 100644 index 0000000000..0f1592b7d0 --- /dev/null +++ b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * 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.alfresco.filesys.avm; + +import java.util.Enumeration; + +import org.alfresco.config.ConfigElement; +import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.auth.InvalidUserException; +import org.alfresco.filesys.server.config.InvalidConfigurationException; +import org.alfresco.filesys.server.config.ServerConfiguration; +import org.alfresco.filesys.server.core.InvalidDeviceInterfaceException; +import org.alfresco.filesys.server.core.ShareMapper; +import org.alfresco.filesys.server.core.ShareType; +import org.alfresco.filesys.server.core.SharedDevice; +import org.alfresco.filesys.server.core.SharedDeviceList; +import org.alfresco.filesys.server.filesys.DiskDeviceContext; +import org.alfresco.filesys.server.filesys.DiskSharedDevice; +import org.alfresco.filesys.server.filesys.SrvDiskInfo; +import org.alfresco.filesys.util.StringList; +import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMWrongTypeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * AVM Filesystem Share Mapper Class + * + *

Provides access to store versions using the share name '_'. + * + * @author gkspencer + */ +public class AVMShareMapper implements ShareMapper { + + // Logging + + private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol"); + + // Regular expression to test for AVM versioned share name + + private static final String AVM_SHAREPATTERN = "[a-zA-Z0-9-]*_[0-9]+"; + + // Server configuration + + private ServerConfiguration m_config; + + // List of available AVM shares + + private StringList m_avmShareNames; + + // Debug enable flag + + private boolean m_debug; + + /** + * Default constructor + */ + public AVMShareMapper() + { + } + + /** + * Initialize the share mapper + * + * @param config ServerConfiguration + * @param params ConfigElement + * @exception InvalidConfigurationException + */ + public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException + { + // Save the server configuration + + m_config = config; + + // Check if debug is enabled + + if (params != null && params.getChild("debug") != null) + m_debug = true; + + // Build the list of available AVM share names + + m_avmShareNames = new StringList(); + + SharedDeviceList shrList = m_config.getShares(); + Enumeration shrEnum = shrList.enumerateShares(); + + while ( shrEnum.hasMoreElements()) + { + // Get the current shared device and check if it is an AVM filesystem device + + SharedDevice curShare = shrEnum.nextElement(); + + try + { + if ( curShare.getInterface() instanceof AVMDiskDriver) + m_avmShareNames.addString( curShare.getName()); + } + catch ( InvalidDeviceInterfaceException ex) + { + } + } + } + + /** + * Check if debug output is enabled + * + * @return boolean + */ + public final boolean hasDebug() + { + return m_debug; + } + + /** + * Return the list of available shares. + * + * @param host String + * @param sess SrvSession + * @param allShares boolean + * @return SharedDeviceList + */ + public SharedDeviceList getShareList(String host, SrvSession sess, boolean allShares) + { + // Make a copy of the global share list and add the per session dynamic shares + + SharedDeviceList shrList = new SharedDeviceList(m_config.getShares()); + + if ( sess != null && sess.hasDynamicShares()) { + + // Add the per session dynamic shares + + shrList.addShares(sess.getDynamicShareList()); + } + + // Remove unavailable shares from the list and return the list + + if ( allShares == false) + shrList.removeUnavailableShares(); + return shrList; + } + + /** + * Find a share using the name and type for the specified client. + * + * @param host String + * @param name String + * @param typ int + * @param sess SrvSession + * @param create boolean + * @return SharedDevice + * @exception InvalidUserException + */ + public SharedDevice findShare(String tohost, String name, int typ, SrvSession sess, boolean create) + throws Exception + { + // Find the required share by name/type. Use a case sensitive search first, if that fails use a case + // insensitive search. + + SharedDevice share = m_config.getShares().findShare(name, typ, false); + + if ( share == null) + { + + // Try a case insensitive search for the required share + + share = m_config.getShares().findShare(name, typ, true); + } + + // If the share was not found then check if the share is in the AVM versioned share format - '_' + + if ( share == null && ( typ == ShareType.DISK || typ == ShareType.UNKNOWN)) + { + // Check if the share has already been created for the session + + if ( sess.hasDynamicShares()) + { + + // Check if the required share exists in the sessions dynamic share list + + share = sess.getDynamicShareList().findShare(name, typ, false); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug(" Reusing existing dynamic share for " + name); + } + + // Check if the share name matches the AVM versioned share name pattern + + if ( share == null && create == true && name.matches( AVM_SHAREPATTERN)) + { + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Map dynamic share " + name + ", type=" + ShareType.TypeAsString(typ)); + + // Split the store name and version id from the share name + + int pos = name.indexOf( '_'); + + String storePath = name.substring(0, pos) + ":/"; + int storeVersion = -1; + + try + { + String storeVer = name.substring( pos + 1); + storeVersion = Integer.parseInt( storeVer); + + if ( storeVersion < 0) + storeVersion = -1; + } + catch ( NumberFormatException ex) + { + logger.error( "Invalid store version id, name=" + name); + } + + // Create the disk driver and context + + if ( storePath.length() > 0 && storeVersion != -1) + { + // Validate the store name and version + + AVMDiskDriver avmDrv = (AVMDiskDriver) m_config.getAvmDiskInterface(); + AVMService avmService = avmDrv.getAvmService(); + + sess.beginTransaction( avmDrv.getTransactionService(), true); + + try + { + // Validate the store name/version + + avmService.lookup( storeVersion, storePath); + + // Create a dynamic share mapped to the AVM store/version + + DiskDeviceContext avmCtx = new AVMContext( storePath, storeVersion); + + // Default the filesystem to look like an 80Gb sized disk with 90% free space + + avmCtx.setDiskInformation(new SrvDiskInfo(2560, 64, 512, 2304)); + + // Create a dynamic shared device for the store version + + DiskSharedDevice diskShare = new DiskSharedDevice( name, avmDrv, avmCtx, SharedDevice.Temporary); + + // Add the new share to the sessions dynamic share list + + sess.addDynamicShare(diskShare); + share = diskShare; + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug(" Mapped share " + name + " - " + diskShare); + } + catch ( AVMNotFoundException ex) + { + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Failed to map share to " + name + ", not such store/version"); + } + catch ( AVMWrongTypeException ex) + { + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Failed to map share to " + name + ", wrong type"); + } + } + } + } + + // Check if the share is available + + if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false) + share = null; + + // Return the shared device, or null if no matching device was found + + return share; + } + + /** + * Delete temporary shares for the specified session + * + * @param sess SrvSession + */ + public void deleteShares(SrvSession sess) + { + + // Check if the session has any dynamic shares + + if ( sess.hasDynamicShares() == false) + return; + + // Delete the dynamic shares + + SharedDeviceList shares = sess.getDynamicShareList(); + Enumeration enm = shares.enumerateShares(); + + while ( enm.hasMoreElements()) { + + // Get the current share from the list + + SharedDevice shr = (SharedDevice) enm.nextElement(); + + // Close the shared device + + shr.getContext().CloseContext(); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("Deleted dynamic share " + shr); + } + + // Clear the dynamic share list + + shares.removeAllShares(); + } + + /** + * Close the share mapper, release any resources. + */ + public void closeMapper() + { + // TODO Auto-generated method stub + + } +} diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index c5e7d433b6..73e78464c0 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -41,6 +41,8 @@ import org.alfresco.config.ConfigLookupContext; import org.alfresco.config.ConfigService; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.avm.AVMContext; +import org.alfresco.filesys.avm.AVMDiskDriver; +import org.alfresco.filesys.avm.AVMShareMapper; import org.alfresco.filesys.ftp.FTPPath; import org.alfresco.filesys.ftp.InvalidPathException; import org.alfresco.filesys.netbios.NetBIOSName; @@ -75,6 +77,7 @@ import org.alfresco.filesys.smb.server.repo.DesktopAction; import org.alfresco.filesys.smb.server.repo.DesktopActionException; import org.alfresco.filesys.smb.server.repo.DesktopActionTable; import org.alfresco.filesys.util.IPAddress; +import org.alfresco.filesys.util.StringList; import org.alfresco.filesys.util.X64; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.NTLMMode; @@ -1773,6 +1776,77 @@ public class ServerConfiguration extends AbstractLifecycleBean logger.warn("No filesystems defined"); } + + // Check if shares should be added for all AVM stores + + ConfigElement avmAllStoresElem = config.getConfigElement( "avmAllStores"); + + if ( avmAllStoresElem != null && getAvmDiskInterface() != null) + { + // Get the list of store names + + AVMDiskDriver avmDriver = (AVMDiskDriver) getAvmDiskInterface(); + StringList storeNames = avmDriver.getAVMStoreNames(); + + // Add shares for each of the store names, if the share name does not already exist + + if ( storeNames != null && storeNames.numberOfStrings() > 0) + { + // Add a share for each store + + for ( int i = 0; i < storeNames.numberOfStrings(); i++) + { + String storeName = storeNames.getStringAt( i); + + // Check if a share of the same name already exists + + if ( getShares().findShare( storeName, ShareType.DISK, true) == null) + { + // Create the new share for the store + + AVMContext avmContext = new AVMContext( storeName + ":/", AVMContext.VERSION_HEAD); + + // Create the shared filesystem + + addShare( new DiskSharedDevice( storeName, avmDriver, avmContext)); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Added AVM share " + storeName); + } + } + } + } + + // Check for the AVM version share mapper + + ConfigElement avmMapperElem = config.getConfigElement( "avmVersionMapper"); + + if ( avmMapperElem != null) + { + try + { + // Create the AVM version share mapper + + AVMShareMapper shareMapper = new AVMShareMapper(); + shareMapper.initializeMapper( this, avmMapperElem); + + // Use the AVM version share mapper + + m_shareMapper = shareMapper; + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Using AVM version share mapper"); + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException("Failed to initialize AVM version share mapper", ex); + } + } + } /**