From e18ab791d704422ad0f147d49afe06f914068baa Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Wed, 2 Nov 2011 23:22:24 +0000 Subject: [PATCH] Merged V3.4-BUG-FIX to HEAD 31072: Incremented version.revision for 3.4.7 31082: Merged DEV to V3.4-BUG-FIX 31077: ALF-9661: Adding or removing associations does not trigger 'update' rule. 1. Unit test for replicating. 2. SingleAssocRefPolicyRuleTrigger now handles trigger of the rules on the list of parents. 31115: Merged DEV/TEMPORARY to V3.4-BUG-FIX 30992: ALF-10029 : Webdav error when document type is missing Correcting directory listing. Display reader's mimetype if display label for mimetype couldn't be found. 31116: Merged DEV/TEMPORARY to V3.4-BUG-FIX 31114: ALF-10309 : CLONE -WebDAV - Canceling "save as" upload will create 0 byte content Logic was added to PutMethod that removes empty files if a save operation is cancelled or fails. 31157: ALF-10757: Merged HEAD to V3.4-BUG-FIX 31154: Synchronize file writes from CIFS. ALF-10721. 31188: Merged DEV/TEMPORARY to V3.4-BUG-FIX 31176: ALF-9979: Error thrown when adding user(s) to group in explorer if user already exists in group Implemented checking of the user presence in the particular group using getAuthService().getContainedAuthorities() 31189: Merged DEV/TEMPORARY to V3.4-BUG-FIX 31174: ALF-9684: CMIS AtomPub: The combination of includeRelationships=both and returnVersion=latestmajor returns an internal server error Try-catch block was added for nodeService.getSourceAssocs(node, RegexQNamePattern.MATCH_ALL) operation. This operation is not supported for a version store. 31190: Merged DEV/TEMPORARY to V3.4-BUG-FIX 20694: ALF-925: "title" property always gets populated with the name of a file when content without a file extension is added via Webdav. When user creates file through webdav, "title" property was populated with "name" property value. The code that was doing it has been removed. Now the title will be empty in such a case. This makes things consistent with CIFS and FTP, where you have to create a rule with action "Extract common metadata fields from content". 31217: ALF-10807: Share document download with SSL - Fix from Bitrock 31226: ALF-10632: Added a sample configuration fragment for war-deployers-jboss-beans.xml because it's rather tricky to publish the correct configuration in the docs! 31265: ALF-9999 Cannot search pdf files, which are written vertically. Sample configures PdfMiner as the pdf -> text and ??? -> pdf -> text transformer rather than PDFBox. PdfMiner is slower but does handle vertical text. 31268: ALF-9093 Alfresco incapable of previewing text files including non Western European characters Sample file that configures OpenOffice as the default handler for text to PDF conversions 31304: Merged BRANCHES/DEV/BELARUS/HEAD-2011_10_05 to BRANCHES/DEV/V3.4-BUG-FIX: 31291: ALF-9460 : Archived discussion posts break display of the discussed node when users do not have access to the trash can 31313: Fix for JBPM security issues with timers and async tasks. ALF-10776: Workflow timer fires a policy that causes a AuthenticationCredentialsNotFoundException ALF-6405: SecureContext issue if task node async property is set to "true" 31314: Fixed ALF-10373 "There is no path to the folder for "Move" action inside the rule's description." 31361: Fixed ALF-10930. Incorrect description of 'xam.archive.forceBackgroundStoreMove' 31366: ALF-3756 - original fix didn't cope with existing MLText having one or more language variants. ...upgrading to the fix would therefore not solve the problem properly. For example, if a property has en_GB text in it, then 'updating' that property with a locale of en_US will result in the addition of the en_US text rather than a true update (they're both English, and using two slightly differently configured browsers in this way leads to confusion). 31387: ALF-10740: Flash upload - Working Copy label not removed on check in if non english locale is used in Share client Two separate problems were fixed: * Locale string as supplied by the flash uploader was being placed into the language portion of the Locale object in java, therefore no proper match was made when looking up the working copy label (e.g. "(Working Copy)") - fixed by using a proper locale string parser as supplied by Surf. * Working copy label could still fail to be removed if document was checked out under one locale and checked in using another. Fix provided by recording the *original* working copy label against the cm:workingcopy aspect. 31395: ALF-3756: fixes broken FullNodeServiceTest 31399: ALF-3756: fixing build failures from -c31366 (VersionServiceImplText.testRevert()) A ConcurrentModificationException was being thrown, have changed the way that the hashmap is processed to avoid this. 31401: Fix for ALF-2980 31410: Fix for ALF-10961 - Share - Sites not displayed on dashboard 31418: ALF-10768 Environment variables are being used to replace property placeholders in xml configuration files Modified ALL spring beans that are a PropertyPlaceholderConfigurer (including sub classes) and the PropertyPlaceholderConfigurer created ChildApplicationContextFactory so that OS environment variables are not used in property place holder substitutions. Spring 3 started allowing OS environment variables to be used by default. This may explain why we have so many install problems. 31420: ALF-3756 another compile error: This time StoreSelectorAspectContentStoreTest 31425: ALF-10740: Fix from -c 31387 (compilation error) This fix is in addition to -c31420. The compilation error was due to me not having the bug fix branch configured as an enterprise project. 31458: Fix for ALF-10916 31459: Fix for ALF-10915 31482: ALF-10915 - Fix build 31491: Various oplock, access mode and CIFS protocol fixes ported from v4/HEAD. ALF-10331. 31492: File open access mode checks ported from v4/HEAD. ALF-10331. 31529: ALF-11144: NPE during incremental LDAP sync 31530: Fixed ALF-11139: Audit concurrency exceptions do not trigger transaction retries - AuditMethodInterceptor allows audit exceptions to propagate out 31606: ALF-10309 CLONE -WebDAV - Cancelling "save as" upload will create 0 byte content Changed name of aspect to be WebDAV specific (rather than using a CIFS value), so that it might be possible to tidy these up in future. 31638: Fix for ALF-10597 - Share - Upload new version issue - OK button displayed during refresh 31639: Fix for ALF-10297 JSF - Sort by First/Last Name in Groups Management not working 31641: ALF-9776 - Fix for WCM - it allows users to submit contents to Staging with past Launch and Expiration Date 31653: ALF-8906: Ensure entire IMAP service startup transaction runs as system, to allow for any auto-aspect adding / versioning during cache warming 31654: Merged V3.4 to V3.4-BUG-FIX 31327: ALF-10878 Merged HEAD to V3.4 30225: ALF-10001 Update SiteService.listMembers to check group display names as well as group names, plus test 31329: Merged V3.4-BUG-FIX (3.4.7) to V3.4 (3.4.6) 30992: ALF-10029 : Webdav error when document type is missing Correcting directory listing. Display reader's mimetype if display label for mimetype couldn't be found. 31462: ALF-10010 Document can not be found even after a full reindex. Sample configures xpdf as the pdf -> text and ??? -> pdf -> text transformer rather than PDFBox. 31478: Merged BELARUS/V3.4-BUG-FIX-2011_10_13 to V3.4 (3.4.6) 31460: ALF-10952: User is unable to edit own profile using Alfresco Share To compare two objects it needs to use equals() method instead of ==. Such a check was changed in UsageQuotaProtector.onUpdateProperties() method. 31502: ALF-10324 Cannot disable Home Folder Creation 2nd part to the problem. Folders were being created when "Show all" was pressed in the UI 31504: Fix for ALF-9589 - Share - Advanced Search is not IE7 compatible for other themes 31531: ALF-10324 Cannot disable Home Folder Creation There is a knock on effect to ContentUsageService when the user does not exist and we are not allowed to create missing users. Resulted in 2 DMDeploymentTargetTest failures 31532: ALF-10955 No pooled task created for moderated site managers if any user requested to join Going with David Wards suggestion of still using getContainingAuthoritiesInZone with a null zone and a cut off at 100. The 100 might be too small, but probably is okay. 31655: ALF-10187: Merged V3.3 to V3.4-BUG-FIX 31180: ALF-9898: CIFS packet pool leaks - File state manager op lock break expiry thread was never getting started so some deferred packets were never getting released! - Synchronize around m_borrowed to prevent ConcurrentModificationExceptions in diagnostics - Synchronize around m_selector.keys() to prevent ConcurrentModificationExceptions during session reaping 31244: Merged DEV/BELARUS/V3.4-BUG-FIX-2011_09_29_CIFS to V3.3 ALF-10831: CIFS concurrency issues under load - Atomic synchronized write AND read operations in FileStateCache, TreeConnection, SrvSessionList, VirtualCircuit, VirtualCircuitList, ContentQuotaManager 31245: Merged DEV/BELARUS/V3.4-BUG-FIX-2011_09_29_CIFS to V3.3 ALF-9540: copy from drive to CIFS is slower than direct drive to drive copy by a factor of ~ 15 - batchRemoveRequest added to ThreadRequestQueue, meaning that not all incoming packets are scatter-gunned across different worker threads and reducing context switching - Arseny's tests show that combined with 3.4.6 changes, the factor is now under 2! 31246: ALF-9898: Merged HEAD to V3.3 31154: Synchronize file writes from CIFS. ALF-10721. 31233: Fixed a possible memory leak in the CIFS oplock timeout handler. 31273: ALF-9540: Reversed batchRemoveRequest changes - Review by Gary. Thread pool may not be utilized properly when multiple virtual connections are being handled at once. 31576: Changes to the CIFS idle session reaper so that it runs in the main socket event thread. ALF-9898. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31657 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/avm-console-context.xml | 3 + config/alfresco/avm-test-context.xml | 3 + config/alfresco/core-services-context.xml | 6 + .../openoffice-text-context.xml.sample | 91 + .../pdfminer-transform-context.xml.sample | 119 + ...-deployers-jboss-beans.xml.fragment.sample | 14 + .../xpdf-transform-context.xml.sample | 105 + config/alfresco/model/contentModel.xml | 6 + .../remote-email-service-test-context.xml | 3 + config/alfresco/rule-services-context.xml | 2 + config/test/alfresco/test-context.xml | 3 + .../wcm-template-node-test-context.xml | 3 + .../cmis/mapping/CMISServicesImpl.java | 8 +- .../config/ServerConfigurationBean.java | 4477 +++++++++-------- .../alfresco/filesys/repo/ContentContext.java | 33 +- .../filesys/repo/ContentDiskDriver.java | 109 +- .../filesys/repo/ContentQuotaManager.java | 145 +- .../repo/audit/AuditMethodInterceptor.java | 9 +- .../repo/coci/CheckOutCheckInServiceImpl.java | 79 +- .../coci/CheckOutCheckInServiceImplTest.java | 75 +- .../alfresco/repo/imap/ImapMessageTest.java | 2 +- .../alfresco/repo/imap/ImapServiceImpl.java | 111 +- .../repo/imap/ImapServiceImplCacheTest.java | 11 +- .../repo/imap/ImapServiceImplTest.java | 2 +- .../org/alfresco/repo/imap/LoadTester.java | 2 +- .../alfresco/repo/jscript/ScriptUtils.java | 5 +- .../ChildApplicationContextFactory.java | 1 + .../repo/node/FullNodeServiceTest.java | 138 +- .../repo/node/MLPropertyInterceptor.java | 35 +- .../repo/rule/RuleServiceCoverageTest.java | 44 + .../SingleAssocRefPolicyRuleTrigger.java | 38 +- .../search/impl/lucene/index/IndexInfo.java | 9 +- .../ChainingUserRegistrySynchronizer.java | 9 +- .../alfresco/repo/site/SiteServiceImpl.java | 1 - .../alfresco/repo/usage/ContentUsageImpl.java | 20 +- .../repo/usage/UsageQuotaProtector.java | 4 +- .../repo/workflow/WorkflowServiceImpl.java | 3 +- .../jbpm/AlfrescoJobExecutorThread.java | 35 + .../repo/workflow/jbpm/AlfrescoTimer.java | 45 +- .../util/ThreadPoolExecutorFactoryBean.java | 9 +- .../test-resources/jbpm-test/test-context.xml | 3 + 41 files changed, 3294 insertions(+), 2526 deletions(-) create mode 100644 config/alfresco/extension/openoffice-text-context.xml.sample create mode 100644 config/alfresco/extension/pdfminer-transform-context.xml.sample create mode 100644 config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample create mode 100644 config/alfresco/extension/xpdf-transform-context.xml.sample diff --git a/config/alfresco/avm-console-context.xml b/config/alfresco/avm-console-context.xml index 76887360ee..facf41e22d 100644 --- a/config/alfresco/avm-console-context.xml +++ b/config/alfresco/avm-console-context.xml @@ -11,6 +11,9 @@ config/alfresco/avm-console.properties + + false + diff --git a/config/alfresco/avm-test-context.xml b/config/alfresco/avm-test-context.xml index febaa0501f..7a92555358 100644 --- a/config/alfresco/avm-test-context.xml +++ b/config/alfresco/avm-test-context.xml @@ -12,6 +12,9 @@ config/alfresco/avm-test.properties + + false + --> diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index bbff0f181c..5186d70392 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -64,6 +64,9 @@ true + + false + @@ -109,6 +112,9 @@ 10 + + false + diff --git a/config/alfresco/extension/openoffice-text-context.xml.sample b/config/alfresco/extension/openoffice-text-context.xml.sample new file mode 100644 index 0000000000..36ffa2d92d --- /dev/null +++ b/config/alfresco/extension/openoffice-text-context.xml.sample @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/extension/pdfminer-transform-context.xml.sample b/config/alfresco/extension/pdfminer-transform-context.xml.sample new file mode 100644 index 0000000000..5907bc8b42 --- /dev/null +++ b/config/alfresco/extension/pdfminer-transform-context.xml.sample @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + + application/pdf + text/plain + + + + + + + + + + + + + + + + + ${python.exe} + ${pdf2txt.py} + + + + + + + 1 + + + + + + + + + + ${python.exe} + ${pdf2txt.py} + -V + -o + ${target} + ${source} + + + + + + 1 + + + + + + + + application/pdf + + + text/plain + + + + + + + diff --git a/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample b/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample new file mode 100644 index 0000000000..3b73824070 --- /dev/null +++ b/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample @@ -0,0 +1,14 @@ + + + + + + + -1 + javax.activation,javax.servlet,javax.servlet.jsp,javax.servlet.jsp.jstl,javax.servlet.jsp.jstl.core,javax.servlet.jsp.jstl.fmt,javax.servlet.jsp.jstl.sql,javax.servlet.jsp.jstl.tlv,javax.xml,javax.xml.bind,javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.bind.helpers,javax.xml.bind.util,javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,javax.xml.crypto.dsig.dom,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec,javax.xml.datatype,javax.xml.messaging,javax.xml.namespace,javax.xml.parsers,javax.xml.rpc,javax.xml.rpc.encoding,javax.xml.rpc.handler,javax.xml.rpc.handler.soap,javax.xml.rpc.holders,javax.xml.rpc.server,javax.xml.rpc.soap,javax.xml.soap,javax.xml.stream,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,javax.xml.validation,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.handler.soap,javax.xml.ws.http,javax.xml.ws.soap,javax.xml.ws.spi,javax.xml.ws.wsaddressing,javax.xml.xpath,org.apache.commons.logging,org.apache.commons.logging.impl,org.apache.xerces,org.apache.xerces.dom,org.apache.xerces.dom.events,org.apache.xerces.dom3,org.apache.xerces.dom3.as,org.apache.xerces.impl,org.apache.xerces.impl.dtd,org.apache.xerces.impl.dtd.models,org.apache.xerces.impl.dv,org.apache.xerces.impl.dv.dtd,org.apache.xerces.impl.dv.util,org.apache.xerces.impl.dv.xs,org.apache.xerces.impl.io,org.apache.xerces.impl.msg,org.apache.xerces.impl.validation,org.apache.xerces.impl.xpath,org.apache.xerces.impl.xpath.regex,org.apache.xerces.impl.xs,org.apache.xerces.impl.xs.identity,org.apache.xerces.impl.xs.models,org.apache.xerces.impl.xs.opti,org.apache.xerces.impl.xs.traversers,org.apache.xerces.impl.xs.util,org.apache.xerces.jaxp,org.apache.xerces.jaxp.datatype,org.apache.xerces.jaxp.validation,org.apache.xerces.parsers,org.apache.xerces.util,org.apache.xerces.xinclude,org.apache.xerces.xni,org.apache.xerces.xni.grammars,org.apache.xerces.xni.parser,org.apache.xerces.xpointer,org.apache.xerces.xs,org.apache.xerces.xs.datatypes,org.apache.xml,org.apache.xml.resolver,org.apache.xml.resolver.apps,org.apache.xml.resolver.etc,org.apache.xml.resolver.etc.catalog.dtd,org.apache.xml.resolver.etc.catalog.rng,org.apache.xml.resolver.etc.catalog.xsd,org.apache.xml.resolver.etc.xcatalog.dtd,org.apache.xml.resolver.helpers,org.apache.xml.resolver.readers,org.apache.xml.resolver.tools,org.apache.xml.security,org.apache.xml.security.algorithms,org.apache.xml.security.algorithms.implementations,org.apache.xml.security.c14n,org.apache.xml.security.c14n.helper,org.apache.xml.security.c14n.implementations,org.apache.xml.security.encryption,org.apache.xml.security.exceptions,org.apache.xml.security.keys,org.apache.xml.security.keys.content,org.apache.xml.security.keys.content.keyvalues,org.apache.xml.security.keys.content.x509,org.apache.xml.security.keys.keyresolver,org.apache.xml.security.keys.keyresolver.implementations,org.apache.xml.security.keys.storage,org.apache.xml.security.keys.storage.implementations,org.apache.xml.security.resource,org.apache.xml.security.resource.schema,org.apache.xml.security.signature,org.apache.xml.security.transforms,org.apache.xml.security.transforms.implementations,org.apache.xml.security.transforms.params,org.apache.xml.security.utils,org.apache.xml.security.utils.resolver,org.apache.xml.security.utils.resolver.implementations,org.apache.xml.serialize,org.apache.xmlcommons,org.xml,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers,org.w3c.css,org.w3c.css.sac,org.w3c.css.sac.helpers,org.w3c.dom,org.w3c.dom.bootstrap,org.w3c.dom.css,org.w3c.dom.events,org.w3c.dom.html,org.w3c.dom.ls,org.w3c.dom.ranges,org.w3c.dom.smil,org.w3c.dom.stylesheets,org.w3c.dom.svg,org.w3c.dom.traversal,org.w3c.dom.views,org.w3c.dom.xpath + + + diff --git a/config/alfresco/extension/xpdf-transform-context.xml.sample b/config/alfresco/extension/xpdf-transform-context.xml.sample new file mode 100644 index 0000000000..9a1459368c --- /dev/null +++ b/config/alfresco/extension/xpdf-transform-context.xml.sample @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + ${pdftotext.root}/pdftotext -enc UTF-8 ${source} ${target} + + + ${pdftotext.root}/pdftotext.exe -enc UTF-8 ${source} ${target} + + + + + + + + + + + + + + + + + chmod 775 ${pdftotext.root}/pdftotext + + + + cmd.exe /C dir + + + + + + + + + + + + + + + application/pdf + + + text/plain + + + + + + + + + + + + transformer.worker.PdfToTextTool + + + + org.alfresco.repo.content.transform.ContentTransformerWorker + + + + + + + + + + + + + + + + + + + + application/pdf + + + + \ No newline at end of file diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index 14430965e2..27d9410705 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -910,6 +910,12 @@ d:text + + + d:text + true + false + diff --git a/config/alfresco/remote-email-service-test-context.xml b/config/alfresco/remote-email-service-test-context.xml index 7a01971001..f780f9dd0d 100644 --- a/config/alfresco/remote-email-service-test-context.xml +++ b/config/alfresco/remote-email-service-test-context.xml @@ -13,6 +13,9 @@ classpath:alfresco/remote-email-service-test.properties + + false + diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index 8563cd291e..8e3bb6cecd 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -75,6 +75,8 @@ + + diff --git a/config/test/alfresco/test-context.xml b/config/test/alfresco/test-context.xml index 68e5910710..b2efdbd0e1 100644 --- a/config/test/alfresco/test-context.xml +++ b/config/test/alfresco/test-context.xml @@ -14,6 +14,9 @@ ${db.url}_test + + false + diff --git a/config/test/alfresco/wcm-template-node-test-context.xml b/config/test/alfresco/wcm-template-node-test-context.xml index d115215c07..bc9a5e62c1 100644 --- a/config/test/alfresco/wcm-template-node-test-context.xml +++ b/config/test/alfresco/wcm-template-node-test-context.xml @@ -8,6 +8,9 @@ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> + + false + diff --git a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java index be1839840a..baba3ac0a8 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java +++ b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java @@ -695,7 +695,13 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware, } if (direction == CMISRelationshipDirectionEnum.TARGET || direction == CMISRelationshipDirectionEnum.EITHER) { - assocs.addAll(nodeService.getSourceAssocs(node, RegexQNamePattern.MATCH_ALL)); + try + { + assocs.addAll(nodeService.getSourceAssocs(node, RegexQNamePattern.MATCH_ALL)); + } + catch (UnsupportedOperationException uoe) { + // NodeServiceImpl#getSourceAssocs - This operation is not supported for a version store + } } // filter association by type diff --git a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java index 9a1e375b08..29f1034f18 100644 --- a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco @@ -14,2232 +14,2240 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.filesys.config; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.Socket; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.UnsupportedCharsetException; -import java.util.EnumSet; -import java.util.Enumeration; -import java.util.List; -import java.util.StringTokenizer; - -import org.springframework.extensions.config.element.GenericConfigElement; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.filesys.AbstractServerConfigurationBean; -import org.alfresco.filesys.alfresco.AlfrescoContext; -import org.alfresco.filesys.alfresco.ExtendedDiskInterface; -import org.alfresco.filesys.avm.AVMContext; -import org.alfresco.filesys.avm.AVMDiskDriver; -import org.alfresco.filesys.config.acl.AccessControlListBean; -import org.alfresco.filesys.repo.ContentContext; -import org.alfresco.jlan.ftp.FTPAuthenticator; -import org.alfresco.jlan.ftp.FTPConfigSection; -import org.alfresco.jlan.ftp.FTPPath; -import org.alfresco.jlan.ftp.InvalidPathException; -import org.alfresco.jlan.netbios.NetBIOSSession; -import org.alfresco.jlan.netbios.RFCNetBIOSProtocol; -import org.alfresco.jlan.netbios.win32.Win32NetBIOS; -import org.alfresco.jlan.oncrpc.RpcAuthenticator; -import org.alfresco.jlan.oncrpc.nfs.NFSConfigSection; -import org.alfresco.jlan.server.auth.ICifsAuthenticator; -import org.alfresco.jlan.server.auth.acl.AccessControlList; -import org.alfresco.jlan.server.auth.passthru.DomainMapping; -import org.alfresco.jlan.server.auth.passthru.RangeDomainMapping; -import org.alfresco.jlan.server.auth.passthru.SubnetDomainMapping; -import org.alfresco.jlan.server.config.CoreServerConfigSection; -import org.alfresco.jlan.server.config.InvalidConfigurationException; -import org.alfresco.jlan.server.config.SecurityConfigSection; -import org.alfresco.jlan.server.core.DeviceContext; -import org.alfresco.jlan.server.core.DeviceContextException; -import org.alfresco.jlan.server.core.ShareMapper; -import org.alfresco.jlan.server.core.ShareType; -import org.alfresco.jlan.server.filesys.DiskDeviceContext; -import org.alfresco.jlan.server.filesys.DiskSharedDevice; -import org.alfresco.jlan.server.filesys.FilesystemsConfigSection; -import org.alfresco.jlan.server.filesys.cache.FileStateLockManager; -import org.alfresco.jlan.server.filesys.cache.StandaloneFileStateCache; -import org.alfresco.jlan.server.filesys.cache.hazelcast.ClusterConfigSection; -import org.alfresco.jlan.server.filesys.cache.hazelcast.HazelCastClusterFileStateCache; -import org.alfresco.jlan.server.thread.ThreadRequestPool; -import org.alfresco.jlan.smb.server.CIFSConfigSection; -import org.alfresco.jlan.util.IPAddress; -import org.alfresco.jlan.util.MemorySize; -import org.alfresco.jlan.util.Platform; -import org.alfresco.jlan.util.StringList; -import org.alfresco.jlan.util.X64; -import org.alfresco.repo.management.subsystems.ActivateableBean; - -import com.hazelcast.core.HazelcastInstance; - -/** - * Alfresco File Server Configuration Bean Class - *

- * Acts as an adaptor between JLAN's configuration requirements and the spring configuration of - * the Alfresco filesystem subsystem. - *

- * Also contains an amount of initialisation logic. - * - * @author gkspencer - * @author dward - * @author mrogers - */ -public class ServerConfigurationBean extends AbstractServerConfigurationBean -{ - private CIFSConfigBean cifsConfigBean; - private FTPConfigBean ftpConfigBean; - private NFSConfigBean nfsConfigBean; - private List filesystemContexts; - private boolean avmAllStores; - private SecurityConfigBean securityConfigBean; - private CoreServerConfigBean coreServerConfigBean; + * along with Alfresco. If not, see . + */ +package org.alfresco.filesys.config; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.List; +import java.util.StringTokenizer; + +import org.springframework.extensions.config.element.GenericConfigElement; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.filesys.AbstractServerConfigurationBean; +import org.alfresco.filesys.alfresco.AlfrescoContext; +import org.alfresco.filesys.alfresco.ExtendedDiskInterface; +import org.alfresco.filesys.avm.AVMContext; +import org.alfresco.filesys.avm.AVMDiskDriver; +import org.alfresco.filesys.config.acl.AccessControlListBean; +import org.alfresco.filesys.repo.ContentContext; +import org.alfresco.jlan.ftp.FTPAuthenticator; +import org.alfresco.jlan.ftp.FTPConfigSection; +import org.alfresco.jlan.ftp.FTPPath; +import org.alfresco.jlan.ftp.InvalidPathException; +import org.alfresco.jlan.netbios.NetBIOSSession; +import org.alfresco.jlan.netbios.RFCNetBIOSProtocol; +import org.alfresco.jlan.netbios.win32.Win32NetBIOS; +import org.alfresco.jlan.oncrpc.RpcAuthenticator; +import org.alfresco.jlan.oncrpc.nfs.NFSConfigSection; +import org.alfresco.jlan.server.auth.ICifsAuthenticator; +import org.alfresco.jlan.server.auth.acl.AccessControlList; +import org.alfresco.jlan.server.auth.passthru.DomainMapping; +import org.alfresco.jlan.server.auth.passthru.RangeDomainMapping; +import org.alfresco.jlan.server.auth.passthru.SubnetDomainMapping; +import org.alfresco.jlan.server.config.CoreServerConfigSection; +import org.alfresco.jlan.server.config.InvalidConfigurationException; +import org.alfresco.jlan.server.config.SecurityConfigSection; +import org.alfresco.jlan.server.core.DeviceContext; +import org.alfresco.jlan.server.core.DeviceContextException; +import org.alfresco.jlan.server.core.ShareMapper; +import org.alfresco.jlan.server.core.ShareType; +import org.alfresco.jlan.server.filesys.DiskDeviceContext; +import org.alfresco.jlan.server.filesys.DiskSharedDevice; +import org.alfresco.jlan.server.filesys.FilesystemsConfigSection; +import org.alfresco.jlan.server.filesys.cache.FileStateLockManager; +import org.alfresco.jlan.server.filesys.cache.StandaloneFileStateCache; +import org.alfresco.jlan.server.filesys.cache.hazelcast.ClusterConfigSection; +import org.alfresco.jlan.server.filesys.cache.hazelcast.HazelCastClusterFileStateCache; +import org.alfresco.jlan.server.thread.ThreadRequestPool; +import org.alfresco.jlan.smb.server.CIFSConfigSection; +import org.alfresco.jlan.util.IPAddress; +import org.alfresco.jlan.util.MemorySize; +import org.alfresco.jlan.util.Platform; +import org.alfresco.jlan.util.StringList; +import org.alfresco.jlan.util.X64; +import org.alfresco.repo.management.subsystems.ActivateableBean; +import org.springframework.beans.factory.DisposableBean; + +import com.hazelcast.core.HazelcastInstance; + +/** + * Alfresco File Server Configuration Bean Class + *

+ * Acts as an adaptor between JLAN's configuration requirements and the spring configuration of + * the Alfresco filesystem subsystem. + *

+ * Also contains an amount of initialisation logic. + * + * @author gkspencer + * @author dward + * @author mrogers + */ +public class ServerConfigurationBean extends AbstractServerConfigurationBean implements DisposableBean +{ + private CIFSConfigBean cifsConfigBean; + private FTPConfigBean ftpConfigBean; + private NFSConfigBean nfsConfigBean; + private List filesystemContexts; + private boolean avmAllStores; + private SecurityConfigBean securityConfigBean; + private CoreServerConfigBean coreServerConfigBean; + + private ThreadRequestPool threadPool; private ClusterConfigBean clusterConfigBean; - - /** - * Default constructor - */ - public ServerConfigurationBean() - { - super(""); - } - - /** - * Class constructor - * - * @param srvName - * String - */ - public ServerConfigurationBean(String srvName) - { - super(srvName); - } - - public void setCifsConfigBean(CIFSConfigBean cifsConfigBean) - { - this.cifsConfigBean = cifsConfigBean; - } - - public void setFtpConfigBean(FTPConfigBean ftpConfigBean) - { - this.ftpConfigBean = ftpConfigBean; - } - - public void setNfsConfigBean(NFSConfigBean nfsConfigBean) - { - this.nfsConfigBean = nfsConfigBean; - } - - public void setFilesystemContexts(List filesystemContexts) - { - this.filesystemContexts = filesystemContexts; - } - - public void setAvmAllStores(boolean avmAllStores) - { - this.avmAllStores = avmAllStores; - } - - public void setSecurityConfigBean(SecurityConfigBean securityConfigBean) - { - this.securityConfigBean = securityConfigBean; - } - - public void setCoreServerConfigBean(CoreServerConfigBean coreServerConfigBean) - { - this.coreServerConfigBean = coreServerConfigBean; - } - - public void setClusterConfigBean(ClusterConfigBean clusterConfigBean) - { - this.clusterConfigBean = clusterConfigBean; - } - - /** - * Process the CIFS server configuration - */ - protected void processCIFSServerConfig() - { - // If the configuration section is not valid then CIFS is disabled - - if (cifsConfigBean == null) - { - removeConfigSection(CIFSConfigSection.SectionName); - return; - } - - // Check if the server has been disabled - if (!cifsConfigBean.getServerEnabled()) - { - removeConfigSection(CIFSConfigSection.SectionName); - return; - } - - // Before we go any further, let's make sure there's a compatible authenticator in the authentication chain. - ICifsAuthenticator authenticator = cifsConfigBean.getAuthenticator(); - if (authenticator == null || authenticator instanceof ActivateableBean && !((ActivateableBean)authenticator).isActive()) - { - logger.warn("No enabled CIFS authenticator found in authentication chain. CIFS Server disabled"); - removeConfigSection(CIFSConfigSection.SectionName); - return; - } - - // Create the CIFS server configuration section - - CIFSConfigSection cifsConfig = new CIFSConfigSection(this); - - try - { - // Check if native code calls should be disabled on Windows - if (cifsConfigBean.getDisableNativeCode()) - { - // Disable native code calls so that the JNI DLL is not required - - cifsConfig.setNativeCodeDisabled(true); - m_disableNativeCode = true; - - // Warning - - logger.warn("CIFS server native calls disabled, JNI code will not be used"); - } - - // Get the network broadcast address - // - // Note: We need to set this first as the call to getLocalDomainName() may use a NetBIOS - // name lookup, so the broadcast mask must be set before then. - - String broadcastAddess = cifsConfigBean.getBroadcastAddress(); - if (broadcastAddess != null && broadcastAddess.length() > 0) - { - - // Check if the broadcast mask is a valid numeric IP address - - if (IPAddress.isNumericAddress(broadcastAddess) == false) - throw new AlfrescoRuntimeException("Invalid broadcast mask, must be n.n.n.n format"); - - // Set the network broadcast mask - - cifsConfig.setBroadcastMask(broadcastAddess); - } - - // Get the host configuration - - String hostName = cifsConfigBean.getServerName(); - if (hostName == null || hostName.length() == 0) - throw new AlfrescoRuntimeException("Host name not specified or invalid"); - - // Check if the host name contains the local name token - - int pos = hostName.indexOf(TokenLocalName); - if (pos != -1) - { - - // Get the local server name - - String srvName = getLocalServerName(true); - - // Rebuild the host name substituting the token with the local server name - - StringBuilder hostStr = new StringBuilder(); - - hostStr.append(hostName.substring(0, pos)); - hostStr.append(srvName); - - pos += TokenLocalName.length(); - if (pos < hostName.length()) - hostStr.append(hostName.substring(pos)); - - hostName = hostStr.toString(); - - // Make sure the CIFS server name does not match the local server name - - if (hostName.equals(srvName) && getPlatformType() == Platform.Type.WINDOWS) - throw new AlfrescoRuntimeException("CIFS server name must be unique"); - } - - // Check if the host name is longer than 15 characters. NetBIOS only allows a maximum of 16 characters in - // the - // server name with the last character reserved for the service type. - - if (hostName.length() > 15) - { - // Truncate the CIFS server name - - hostName = hostName.substring(0, 15); - - // Output a warning - - logger.warn("CIFS server name is longer than 15 characters, truncated to " + hostName); - } - - // Set the CIFS server name - - cifsConfig.setServerName(hostName.toUpperCase()); - setServerName(hostName.toUpperCase()); - - // Get the domain/workgroup name - - String domain = cifsConfigBean.getDomainName(); - if (domain != null && domain.length() > 0) - { - // Set the domain/workgroup name - - cifsConfig.setDomainName(domain.toUpperCase()); - } - else - { - // Get the local domain/workgroup name - - String localDomain = getLocalDomainName(); - - if (localDomain == null && (getPlatformType() != Platform.Type.WINDOWS || isNativeCodeDisabled())) - { - // Use a default domain/workgroup name - - localDomain = "WORKGROUP"; - - // Output a warning - - logger.error("Failed to get local domain/workgroup name, using default of " + localDomain); - logger.error("(This may be due to firewall settings or incorrect setting)"); - } - - // Set the local domain/workgroup that the CIFS server belongs to - - cifsConfig.setDomainName(localDomain); - } - - // Check for a server comment - String comment = cifsConfigBean.getServerComment(); - if (comment != null && comment.length() > 0) - { - cifsConfig.setComment(comment); - } - - // Check for a bind address - - // Check if the network adapter name has been specified - String bindToAdapter = cifsConfigBean.getBindToAdapter(); - String bindTo; - - if (bindToAdapter != null && bindToAdapter.length() > 0) - { - - // Get the IP address for the adapter - - InetAddress bindAddr = parseAdapterName(bindToAdapter); - - // Set the bind address for the server - - cifsConfig.setSMBBindAddress(bindAddr); - } - else if ((bindTo = cifsConfigBean.getBindToAddress()) != null && bindTo.length() > 0 - && !bindTo.equals(BIND_TO_IGNORE)) - { - - // Validate the bind address - try - { - // Check the bind address - - InetAddress bindAddr = InetAddress.getByName(bindTo); - - // Set the bind address for the server - - cifsConfig.setSMBBindAddress(bindAddr); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid CIFS server bind address"); - } - } - - // Get the authenticator - - if (authenticator != null) - { - cifsConfig.setAuthenticator(authenticator); - } - else - throw new AlfrescoRuntimeException("CIFS authenticator not specified"); - - // Check if the host announcer has been disabled - - if (!cifsConfigBean.getHostAccouncerEnabled()) - { - // Switch off the host announcer - - cifsConfig.setHostAnnouncer( false); - - // Log that host announcements are not enabled - - logger.info("Host announcements not enabled"); - } - else - { - // Check for an announcement interval - - Integer interval = cifsConfigBean.getHostAccounceInterval(); - if (interval != null) - { - cifsConfig.setHostAnnounceInterval(interval); - } - - // Check if the domain name has been set, this is required if the - // host announcer is enabled - - if (cifsConfig.getDomainName() == null) - throw new AlfrescoRuntimeException("Domain name must be specified if host announcement is enabled"); - - // Enable host announcement - - cifsConfig.setHostAnnouncer(true); - } - - // Check if NetBIOS SMB is enabled - NetBIOSSMBConfigBean netBIOSSMBConfigBean = cifsConfigBean.getNetBIOSSMB(); - if (netBIOSSMBConfigBean != null) - { - // Check if NetBIOS over TCP/IP is enabled for the current platform - - String platformsStr = netBIOSSMBConfigBean.getPlatforms(); - boolean platformOK = false; - - if (platformsStr != null && platformsStr.length() > 0) - { - // Parse the list of platforms that NetBIOS over TCP/IP is to be enabled for and - // check if the current platform is included - - EnumSet enabledPlatforms = parsePlatformString(platformsStr); - if (enabledPlatforms.contains(getPlatformType())) - platformOK = true; - } - else - { - // No restriction on platforms - - platformOK = true; - } - - // Enable the NetBIOS SMB support, if enabled for this platform - - cifsConfig.setNetBIOSSMB(platformOK); - - // Parse/check NetBIOS settings, if enabled - - if (cifsConfig.hasNetBIOSSMB()) - { - // Check if the broadcast mask has been specified - - if (cifsConfig.getBroadcastMask() == null) - throw new AlfrescoRuntimeException("Network broadcast mask not specified"); - - // Check for a bind address - - String bindto = netBIOSSMBConfigBean.getBindTo(); - if (bindto != null && bindto.length() > 0 && !bindto.equals(BIND_TO_IGNORE)) - { - - // Validate the bind address - - try - { - - // Check the bind address - - InetAddress bindAddr = InetAddress.getByName(bindto); - - // Set the bind address for the NetBIOS name server - - cifsConfig.setNetBIOSBindAddress(bindAddr); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid NetBIOS bind address"); - } - } - else if (cifsConfig.hasSMBBindAddress()) - { - - // Use the SMB bind address for the NetBIOS name server - - cifsConfig.setNetBIOSBindAddress(cifsConfig.getSMBBindAddress()); - } - else - { - // Get a list of all the local addresses - - InetAddress[] addrs = null; - - try - { - // Get the local server IP address list - - addrs = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName()); - } - catch (UnknownHostException ex) - { - logger.error("Failed to get local address list", ex); - } - - // Check the address list for one or more valid local addresses filtering out the loopback - // address - - int addrCnt = 0; - - if (addrs != null) - { - for (int i = 0; i < addrs.length; i++) - { - - // Check for a valid address, filter out '127.0.0.1' and '0.0.0.0' addresses - - if (addrs[i].getHostAddress().equals("127.0.0.1") == false - && addrs[i].getHostAddress().equals("0.0.0.0") == false) - addrCnt++; - } - } - - // Check if any addresses were found - - if (addrCnt == 0) - { - // Enumerate the network adapter list - - Enumeration niEnum = null; - - try - { - niEnum = NetworkInterface.getNetworkInterfaces(); - } - catch (SocketException ex) - { - } - - if (niEnum != null) - { - while (niEnum.hasMoreElements()) - { - // Get the current network interface - - NetworkInterface ni = niEnum.nextElement(); - - // Enumerate the addresses for the network adapter - - Enumeration niAddrs = ni.getInetAddresses(); - if (niAddrs != null) - { - // Check for any valid addresses - - while (niAddrs.hasMoreElements()) - { - InetAddress curAddr = niAddrs.nextElement(); - - if (curAddr.getHostAddress().equals("127.0.0.1") == false - && curAddr.getHostAddress().equals("0.0.0.0") == false) - addrCnt++; - } - } - } - - // DEBUG - - if (addrCnt > 0 && logger.isDebugEnabled()) - logger.debug("Found valid IP address from interface list"); - } - - // Check if we found any valid network addresses - - if (addrCnt == 0) - { - // Log the available IP addresses - - if (logger.isDebugEnabled()) - { - logger.debug("Local address list dump :-"); - if (addrs != null) - { - for (int i = 0; i < addrs.length; i++) - logger.debug(" Address: " + addrs[i]); - } - else - logger.debug(" No addresses"); - } - - // Throw an exception to stop the CIFS/NetBIOS name server from starting - - throw new AlfrescoRuntimeException( - "Failed to get IP address(es) for the local server, check hosts file and/or DNS setup"); - } - } - } - - // Check if the session port has been specified - - Integer portNum = netBIOSSMBConfigBean.getSessionPort(); - if (portNum != null) - { - cifsConfig.setSessionPort(portNum); - if (cifsConfig.getSessionPort() <= 0 || cifsConfig.getSessionPort() >= 65535) - throw new AlfrescoRuntimeException("NetBIOS session port out of valid range"); - } - - // Check if the name port has been specified - - portNum = netBIOSSMBConfigBean.getNamePort(); - if (portNum != null) - { - cifsConfig.setNameServerPort(portNum); - if (cifsConfig.getNameServerPort() <= 0 || cifsConfig.getNameServerPort() >= 65535) - throw new AlfrescoRuntimeException("NetBIOS name port out of valid range"); - } - - // Check if the datagram port has been specified - - portNum = netBIOSSMBConfigBean.getDatagramPort(); - if (portNum != null) - { - cifsConfig.setDatagramPort(portNum); - if (cifsConfig.getDatagramPort() <= 0 || cifsConfig.getDatagramPort() >= 65535) - throw new AlfrescoRuntimeException("NetBIOS datagram port out of valid range"); - } - - // Check for a bind address - - String attr = netBIOSSMBConfigBean.getBindTo(); - if (attr != null && attr.length() > 0 && !attr.equals(BIND_TO_IGNORE)) - { - - // Validate the bind address - - try - { - - // Check the bind address - - InetAddress bindAddr = InetAddress.getByName(attr); - - // Set the bind address for the NetBIOS name server - - cifsConfig.setNetBIOSBindAddress(bindAddr); - } - catch (UnknownHostException ex) - { - throw new InvalidConfigurationException(ex.toString()); - } - } - - // Check for a bind address using the adapter name - - else if ((attr = netBIOSSMBConfigBean.getAdapter()) != null && attr.length() > 0) - { - - // Get the bind address via the network adapter name - - InetAddress bindAddr = parseAdapterName(attr); - cifsConfig.setNetBIOSBindAddress(bindAddr); - } - else if (cifsConfig.hasSMBBindAddress()) - { - - // Use the SMB bind address for the NetBIOS name server - - cifsConfig.setNetBIOSBindAddress(cifsConfig.getSMBBindAddress()); - } - - } - } - else - { - - // Disable NetBIOS SMB support - - cifsConfig.setNetBIOSSMB(false); - } - - // Check if TCP/IP SMB is enabled - - TcpipSMBConfigBean tcpipSMBConfigBean = cifsConfigBean.getTcpipSMB(); - if (tcpipSMBConfigBean != null) - { - - // Check if native SMB is enabled for the current platform - - String platformsStr = tcpipSMBConfigBean.getPlatforms(); - boolean platformOK = false; - - if (platformsStr != null) - { - // Parse the list of platforms that native SMB is to be enabled for and - // check if the current platform is included - - EnumSet enabledPlatforms = parsePlatformString(platformsStr); - if (enabledPlatforms.contains(getPlatformType())) - platformOK = true; - } - else - { - // No restriction on platforms - - platformOK = true; - } - - // Enable the TCP/IP SMB support, if enabled for this platform - - cifsConfig.setTcpipSMB(platformOK); - - // Check if the port has been specified - - Integer portNum = tcpipSMBConfigBean.getPort(); - if (portNum != null) - { - cifsConfig.setTcpipSMBPort(portNum); - if (cifsConfig.getTcpipSMBPort() <= 0 || cifsConfig.getTcpipSMBPort() >= 65535) - throw new AlfrescoRuntimeException("TCP/IP SMB port out of valid range"); - } - - // Check if IPv6 support should be enabled - - if ( tcpipSMBConfigBean.getIpv6Enabled()) - { - try - { - // Use the IPv6 bind all address - - cifsConfig.setSMBBindAddress( InetAddress.getByName( "::")); - - // DEBUG - - if ( logger.isInfoEnabled()) - logger.info("Enabled CIFS IPv6 bind address for native SMB"); - } - catch ( UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Failed to enable IPv6 bind address, " + ex.getMessage()); - } - } - - } - else - { - - // Disable TCP/IP SMB support - - cifsConfig.setTcpipSMB(false); - } - - // Check if Win32 NetBIOS is enabled - - Win32NetBIOSConfigBean win32NetBIOSConfigBean = cifsConfigBean.getWin32NetBIOS(); - if (win32NetBIOSConfigBean != null) - { - - // Check if the Win32 NetBIOS server name has been specified - - String win32Name = win32NetBIOSConfigBean.getName(); - if (win32Name != null && win32Name.length() > 0) - { - - // Validate the name - - if (win32Name.length() > 16) - throw new AlfrescoRuntimeException("Invalid Win32 NetBIOS name, " + win32Name); - - // Set the Win32 NetBIOS file server name - - cifsConfig.setWin32NetBIOSName(win32Name); - } - - // Check if the Win32 NetBIOS LANA has been specified - - String lanaStr = win32NetBIOSConfigBean.getLana(); - if (lanaStr != null && lanaStr.length() > 0) - { - // Check if the LANA has been specified as an IP address or adapter name - - int lana = -1; - - if (IPAddress.isNumericAddress(lanaStr)) - { - - // Convert the IP address to a LANA id - - lana = Win32NetBIOS.getLANAForIPAddress(lanaStr); - if (lana == -1) - throw new AlfrescoRuntimeException("Failed to convert IP address " + lanaStr + " to a LANA"); - } - else if (lanaStr.length() > 1 && Character.isLetter(lanaStr.charAt(0))) - { - - // Convert the network adapter to a LANA id - - lana = Win32NetBIOS.getLANAForAdapterName(lanaStr); - if (lana == -1) - throw new AlfrescoRuntimeException("Failed to convert network adapter " + lanaStr - + " to a LANA"); - } - else - { - - try - { - lana = Integer.parseInt(lanaStr); - } - catch (NumberFormatException ex) - { - throw new AlfrescoRuntimeException("Invalid win32 NetBIOS LANA specified"); - } - } - - // LANA should be in the range 0-255 - - if (lana < 0 || lana > 255) - throw new AlfrescoRuntimeException("Invalid Win32 NetBIOS LANA number, " + lana); - - // Set the LANA number - - cifsConfig.setWin32LANA(lana); - } - - // Check if the native NetBIOS interface has been specified, either 'winsock' or 'netbios' - - String nativeAPI = win32NetBIOSConfigBean.getApi(); - if (nativeAPI != null && nativeAPI.length() > 0) - { - // Validate the API type - - boolean useWinsock = true; - - if (nativeAPI.equalsIgnoreCase("netbios")) - useWinsock = false; - else if (nativeAPI.equalsIgnoreCase("winsock") == false) - throw new AlfrescoRuntimeException("Invalid NetBIOS API type, spefify 'winsock' or 'netbios'"); - - // Set the NetBIOS API to use - - cifsConfig.setWin32WinsockNetBIOS(useWinsock); - } - - // Force the older NetBIOS API code to be used on 64Bit Windows - - if (cifsConfig.useWinsockNetBIOS() == true && X64.isWindows64()) - { - // Debug - - if (logger.isDebugEnabled()) - logger.debug("Using older Netbios() API code"); - - // Use the older NetBIOS API code - - cifsConfig.setWin32WinsockNetBIOS(false); - } - - // Check if the current operating system is supported by the Win32 - // NetBIOS handler - - String osName = System.getProperty("os.name"); - if (osName.startsWith("Windows") - && (osName.endsWith("95") == false && osName.endsWith("98") == false && osName.endsWith("ME") == false) - && isNativeCodeDisabled() == false) - { - - // Call the Win32NetBIOS native code to make sure it is initialized - - if (Win32NetBIOS.LanaEnumerate() != null) - { - // Enable Win32 NetBIOS - - cifsConfig.setWin32NetBIOS(true); - } - else - { - logger.warn("No NetBIOS LANAs available"); - } - } - else - { - - // Win32 NetBIOS not supported on the current operating system - - cifsConfig.setWin32NetBIOS(false); - } - } - else - { - - // Disable Win32 NetBIOS - - cifsConfig.setWin32NetBIOS(false); - } - - // Check if the Win32 host announcer has been disabled - - if ( !cifsConfigBean.getWin32HostAnnouncerEnabled()) - { - // Switch off the Win32 host announcer - - cifsConfig.setWin32HostAnnouncer( false); - - // Log that host announcements are not enabled - - logger.info("Win32 host announcements not enabled"); - } - else - { - // Check for an announcement interval - Integer interval = cifsConfigBean.getWin32HostAnnounceInterval(); - if (interval != null) - { - cifsConfig.setWin32HostAnnounceInterval(interval); - } - - // Check if the domain name has been set, this is required if the - // host announcer is enabled - - if (cifsConfig.getDomainName() == null) - throw new AlfrescoRuntimeException("Domain name must be specified if host announcement is enabled"); - - // Enable Win32 NetBIOS host announcement - - cifsConfig.setWin32HostAnnouncer(true); - } - - // Check if NetBIOS and/or TCP/IP SMB have been enabled - - if (cifsConfig.hasNetBIOSSMB() == false && cifsConfig.hasTcpipSMB() == false - && cifsConfig.hasWin32NetBIOS() == false) - throw new AlfrescoRuntimeException("NetBIOS SMB, TCP/IP SMB or Win32 NetBIOS must be enabled"); - - // Check if WINS servers are configured - - WINSConfigBean winsConfigBean = cifsConfigBean.getWINSConfig(); - - if (winsConfigBean != null && !winsConfigBean.isAutoDetectEnabled()) - { - - // Get the primary WINS server - - String priWins = winsConfigBean.getPrimary(); - - if (priWins == null || priWins.length() == 0) - throw new AlfrescoRuntimeException("No primary WINS server configured"); - - // Validate the WINS server address - - InetAddress primaryWINS = null; - - try - { - primaryWINS = InetAddress.getByName(priWins); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid primary WINS server address, " + priWins); - } - - // Check if a secondary WINS server has been specified - - String secWins = winsConfigBean.getSecondary(); - InetAddress secondaryWINS = null; - - if (secWins != null && secWins.length() > 0) - { - - // Validate the secondary WINS server address - - try - { - secondaryWINS = InetAddress.getByName(secWins); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid secondary WINS server address, " + secWins); - } - } - - // Set the WINS server address(es) - - cifsConfig.setPrimaryWINSServer(primaryWINS); - if (secondaryWINS != null) - cifsConfig.setSecondaryWINSServer(secondaryWINS); - - // Pass the setting to the NetBIOS session class - - NetBIOSSession.setDefaultWINSServer(primaryWINS); - } - - // Check if WINS is configured, if we are running on Windows and socket based NetBIOS is enabled - - else if (cifsConfig.hasNetBIOSSMB() && getPlatformType() == Platform.Type.WINDOWS && !isNativeCodeDisabled()) - { - // Get the WINS server list - - String winsServers = Win32NetBIOS.getWINSServerList(); - - if (winsServers != null) - { - // Use the first WINS server address for now - - StringTokenizer tokens = new StringTokenizer(winsServers, ","); - String addr = tokens.nextToken(); - - try - { - // Convert to a network address and check if the WINS server is accessible - - InetAddress winsAddr = InetAddress.getByName(addr); - - Socket winsSocket = new Socket(); - InetSocketAddress sockAddr = new InetSocketAddress(winsAddr, RFCNetBIOSProtocol.NAME_PORT); - - winsSocket.connect(sockAddr, 3000); - winsSocket.close(); - - // Set the primary WINS server address - - cifsConfig.setPrimaryWINSServer(winsAddr); - - // Debug - - if (logger.isDebugEnabled()) - logger.debug("Configuring to use WINS server " + addr); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid auto WINS server address, " + addr); - } - catch (IOException ex) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to connect to auto WINS server " + addr); - } - } - } - - // Check for session debug flags - - String flags = cifsConfigBean.getSessionDebugFlags(); - - int sessDbg = 0; - - if (flags != null && flags.length() > 0) - { - - // Parse the flags - - flags = flags.toUpperCase(); - StringTokenizer token = new StringTokenizer(flags, ","); - - while (token.hasMoreTokens()) - { - // Get the current debug flag token - - String dbg = token.nextToken().trim(); - - // Find the debug flag name - - int idx = 0; - - while (idx < m_sessDbgStr.length && m_sessDbgStr[idx].equalsIgnoreCase(dbg) == false) - idx++; - - if (idx > m_sessDbgStr.length) - throw new AlfrescoRuntimeException("Invalid session debug flag, " + dbg); - - // Set the debug flag - - sessDbg += 1 << idx; - } - } - - // Set the session debug flags - - cifsConfig.setSessionDebugFlags(sessDbg); - - // Check if NIO based socket code should be disabled - - if (cifsConfigBean.getDisableNIO()) - { - - // Disable NIO based code - - cifsConfig.setDisableNIOCode(true); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("NIO based code disabled for CIFS server"); - } - - - // Check if a session timeout is configured - - Integer tmo = cifsConfigBean.getSessionTimeout(); - if (tmo != null) - { - - // Validate the session timeout value - - if (tmo < 0 || tmo > MaxSessionTimeout) - throw new AlfrescoRuntimeException("Session timeout out of range (0 - " + MaxSessionTimeout + ")"); - - // Convert the session timeout to milliseconds - - cifsConfig.setSocketTimeout(tmo * 1000); - } - } - catch (InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException(ex.getMessage()); - } - } - - /** - * Process the FTP server configuration - */ - protected void processFTPServerConfig() - { - // If the configuration section is not valid then FTP is disabled - - if (ftpConfigBean == null) - { - removeConfigSection(FTPConfigSection.SectionName); - return; - } - - // Check if the server has been disabled - - if (!ftpConfigBean.getServerEnabled()) - { - removeConfigSection(FTPConfigSection.SectionName); - return; - } - - // Create the FTP configuration section - - FTPConfigSection ftpConfig = new FTPConfigSection(this); - - try - { - // Check for a bind address - - String bindText = ftpConfigBean.getBindTo(); - if (bindText != null && bindText.length() > 0 && !bindText.equals(BIND_TO_IGNORE)) - { - - // Validate the bind address - - try - { - - // Check the bind address - - InetAddress bindAddr = InetAddress.getByName(bindText); - - // Set the bind address for the FTP server - - ftpConfig.setFTPBindAddress(bindAddr); - } - catch (UnknownHostException ex) - { - throw new AlfrescoRuntimeException("Invalid FTP bindto address, " + bindText); - } - } - - // Check for an FTP server port - - Integer port = ftpConfigBean.getPort(); - if (port != null) - { - ftpConfig.setFTPPort(port); - if (ftpConfig.getFTPPort() <= 0 || ftpConfig.getFTPPort() >= 65535) - throw new AlfrescoRuntimeException("FTP server port out of valid range"); - } - else - { - - // Use the default FTP port - - ftpConfig.setFTPPort(DefaultFTPServerPort); - } - - // Check if anonymous login is allowed - - if (ftpConfigBean.getAllowAnonymous()) - { - - // Enable anonymous login to the FTP server - - ftpConfig.setAllowAnonymousFTP(true); - - // Check if an anonymous account has been specified - - String anonAcc = ftpConfigBean.getAnonymousAccount(); - if (anonAcc != null && anonAcc.length() > 0) - { - - // Set the anonymous account name - - ftpConfig.setAnonymousFTPAccount(anonAcc); - - // Check if the anonymous account name is valid - - if (ftpConfig.getAnonymousFTPAccount() == null || ftpConfig.getAnonymousFTPAccount().length() == 0) - throw new AlfrescoRuntimeException("Anonymous FTP account invalid"); - } - else - { - - // Use the default anonymous account name - - ftpConfig.setAnonymousFTPAccount(DefaultFTPAnonymousAccount); - } - } - else - { - - // Disable anonymous logins - - ftpConfig.setAllowAnonymousFTP(false); - } - - // Check if a root path has been specified - - String rootPath = ftpConfigBean.getRootDirectory(); - if (rootPath != null && rootPath.length() > 0) - { - try - { - - // Parse the path - - new FTPPath(rootPath); - - // Set the root path - - ftpConfig.setFTPRootPath(rootPath); - } - catch (InvalidPathException ex) - { - throw new AlfrescoRuntimeException("Invalid FTP root directory, " + rootPath); - } - } - - // Check for FTP debug flags - - String flags = ftpConfigBean.getDebugFlags(); - int ftpDbg = 0; - - if (flags != null) - { - - // Parse the flags - - flags = flags.toUpperCase(); - StringTokenizer token = new StringTokenizer(flags, ","); - - while (token.hasMoreTokens()) - { - - // Get the current debug flag token - - String dbg = token.nextToken().trim(); - - // Find the debug flag name - - int idx = 0; - - while (idx < m_ftpDebugStr.length && m_ftpDebugStr[idx].equalsIgnoreCase(dbg) == false) - idx++; - - if (idx >= m_ftpDebugStr.length) - throw new AlfrescoRuntimeException("Invalid FTP debug flag, " + dbg); - - // Set the debug flag - - ftpDbg += 1 << idx; - } - - // Set the FTP debug flags - - ftpConfig.setFTPDebug(ftpDbg); - } - - // Check if a character set has been specified - - String charSet = ftpConfigBean.getCharSet(); - if (charSet != null && charSet.length() > 0) - { - - try - { - - // Validate the character set name - - Charset.forName(charSet); - - // Set the FTP character set - - ftpConfig.setFTPCharacterSet(charSet); - } - catch (IllegalCharsetNameException ex) - { - throw new AlfrescoRuntimeException("Illegal character set name, " + charSet); - } - catch (UnsupportedCharsetException ex) - { - throw new AlfrescoRuntimeException("Unsupported character set name, " + charSet); - } - } - - // Check if an authenticator has been specified - - FTPAuthenticator auth = ftpConfigBean.getAuthenticator(); - if (auth != null) - { - - // Initialize and set the authenticator class - - ftpConfig.setAuthenticator(auth); - } - else - throw new AlfrescoRuntimeException("FTP authenticator not specified"); - - // Check if a data port range has been specified - - if ( ftpConfigBean.getDataPortFrom() != 0 && ftpConfigBean.getDataPortTo() != 0) { - - // Range check the data port values - - int rangeFrom = ftpConfigBean.getDataPortFrom(); - int rangeTo = ftpConfigBean.getDataPortTo(); - - if ( rangeFrom != 0 && rangeTo != 0) { - - // Validate the FTP data port range - - if ( rangeFrom < 1024 || rangeFrom > 65535) - throw new InvalidConfigurationException("Invalid FTP data port range from value, " + rangeFrom); - - if ( rangeTo < 1024 || rangeTo > 65535) - throw new InvalidConfigurationException("Invalid FTP data port range to value, " + rangeTo); - - if ( rangeFrom >= rangeTo) - throw new InvalidConfigurationException("Invalid FTP data port range, " + rangeFrom + "-" + rangeTo); - - // Set the FTP data port range - - ftpConfig.setFTPDataPortLow(rangeFrom); - ftpConfig.setFTPDataPortHigh(rangeTo); - - // Log the data port range - - logger.info("FTP server data ports restricted to range " + rangeFrom + ":" + rangeTo); - } - } - - // FTPS parameter parsing - // - // Check if a key store path has been specified - - if ( ftpConfigBean.getKeyStorePath() != null && ftpConfigBean.getKeyStorePath().length() > 0) { - - // Get the path to the key store, check that the file exists - - String keyStorePath = ftpConfigBean.getKeyStorePath(); - File keyStoreFile = new File( keyStorePath); - - if ( keyStoreFile.exists() == false) - throw new InvalidConfigurationException("FTPS key store file does not exist, " + keyStorePath); - else if ( keyStoreFile.isDirectory()) - throw new InvalidConfigurationException("FTPS key store path is a directory, " + keyStorePath); - - // Set the key store path - - ftpConfig.setKeyStorePath( keyStorePath); - } - - // Check if the trust store path has been specified - - if ( ftpConfigBean.getTrustStorePath() != null && ftpConfigBean.getTrustStorePath().length() > 0) { - - // Get the path to the trust store, check that the file exists - - String trustStorePath = ftpConfigBean.getTrustStorePath(); - File trustStoreFile = new File( trustStorePath); - - if ( trustStoreFile.exists() == false) - throw new InvalidConfigurationException("FTPS trust store file does not exist, " + trustStorePath); - else if ( trustStoreFile.isDirectory()) - throw new InvalidConfigurationException("FTPS trust store path is a directory, " + trustStorePath); - - // Set the trust store path - - ftpConfig.setTrustStorePath( trustStorePath); - } - - // Check if the store passphrase has been specified - - if ( ftpConfigBean.getPassphrase() != null && ftpConfigBean.getPassphrase().length() > 0) { - - // Set the store passphrase - - ftpConfig.setPassphrase( ftpConfigBean.getPassphrase()); - } - - // Check if only secure sessions should be allowed to logon - - if ( ftpConfigBean.hasRequireSecureSession()) { - - // Only allow secure sessions to logon to the FTP server - - ftpConfig.setRequireSecureSession( true); - } - - // Check that all the required FTPS parameters have been set - - if ( ftpConfig.getKeyStorePath() != null || ftpConfig.getTrustStorePath() != null || ftpConfig.getPassphrase() != null) { - - // Make sure all parameters are set - - if ( ftpConfig.getKeyStorePath() == null || ftpConfig.getTrustStorePath() == null || ftpConfig.getPassphrase() == null) - throw new InvalidConfigurationException("FTPS configuration requires keyStore, trustStore and storePassphrase to be set"); - } - - // Check if SSLEngine debug output should be enabled - - if ( ftpConfigBean.hasSslEngineDebug()) { - - // Enable SSLEngine debug output - - System.setProperty("javax.net.debug", "ssl,handshake"); - } - } - catch (InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException(ex.getMessage()); - } - } - - /** - * Process the NFS server configuration - */ - protected void processNFSServerConfig() - { - // If the configuration section is not valid then NFS is disabled - - if (nfsConfigBean == null) - { - removeConfigSection(NFSConfigSection.SectionName); - return; - } - - // Check if the server has been disabled - - if (!nfsConfigBean.getServerEnabled()) - { - removeConfigSection(NFSConfigSection.SectionName); - return; - } - - // Create the NFS configuration section - - NFSConfigSection nfsConfig = new NFSConfigSection(this); - - try - { - // Check if the port mapper is enabled - - if (nfsConfigBean.getPortMapperEnabled()) - nfsConfig.setNFSPortMapper(true); - - // Check for the thread pool size - - Integer poolSize = nfsConfigBean.getThreadPool(); - - if (poolSize != null) - { - - // Range check the pool size value - - if (poolSize < 4) - throw new AlfrescoRuntimeException("NFS thread pool size is below minimum of 4"); - - // Set the thread pool size - - nfsConfig.setNFSThreadPoolSize(poolSize); - } - - // NFS packet pool size - - Integer pktPoolSize = nfsConfigBean.getPacketPool(); - - if (pktPoolSize != null) - { - // Range check the pool size value - - if (pktPoolSize < 10) - throw new AlfrescoRuntimeException("NFS packet pool size is below minimum of 10"); - - if (pktPoolSize < nfsConfig.getNFSThreadPoolSize() + 1) - throw new AlfrescoRuntimeException("NFS packet pool must be at least thread pool size plus one"); - - // Set the packet pool size - - nfsConfig.setNFSPacketPoolSize(pktPoolSize); - } - - // Check for a port mapper server port - - Integer portMapperPort = nfsConfigBean.getPortMapperPort(); - if (portMapperPort != null) - { - nfsConfig.setPortMapperPort(portMapperPort); - if ( nfsConfig.getPortMapperPort() == -1) { - logger.info("NFS portmapper registration disabled"); - } - else { - if (nfsConfig.getPortMapperPort() <= 0 || nfsConfig.getPortMapperPort() >= 65535) - throw new AlfrescoRuntimeException("Port mapper server port out of valid range"); - } - } - - // Check for a mount server port - - Integer mountServerPort = nfsConfigBean.getMountServerPort(); - if (mountServerPort != null) - { - nfsConfig.setMountServerPort(mountServerPort); - if (nfsConfig.getMountServerPort() < 0 || nfsConfig.getMountServerPort() >= 65535) - throw new AlfrescoRuntimeException("Mount server port out of valid range"); - } - - // Check for an NFS server port - - Integer nfsServerPort = nfsConfigBean.getNfsServerPort(); - if (nfsServerPort != null) - { - nfsConfig.setNFSServerPort(nfsServerPort); - if (nfsConfig.getNFSServerPort() < 0 || nfsConfig.getNFSServerPort() >= 65535) - throw new AlfrescoRuntimeException("NFS server port out of valid range"); - } - - // Check for an RPC registration port - - Integer rpcRegisterPort = nfsConfigBean.getRpcRegisterPort(); - if ( rpcRegisterPort != null) - { - nfsConfig.setRPCRegistrationPort( rpcRegisterPort); - if ( nfsConfig.getRPCRegistrationPort() < 0 || nfsConfig.getRPCRegistrationPort() >= 65535) - throw new AlfrescoRuntimeException("RPC registrtion port out of valid range"); - } - - // Check for NFS debug flags - - String flags = nfsConfigBean.getDebugFlags(); - int nfsDbg = 0; - - if (flags != null && flags.length() > 0) - { - - // Parse the flags - - flags = flags.toUpperCase(); - StringTokenizer token = new StringTokenizer(flags, ","); - - while (token.hasMoreTokens()) - { - - // Get the current debug flag token - - String dbg = token.nextToken().trim(); - - // Find the debug flag name - - int idx = 0; - - while (idx < m_nfsDebugStr.length && m_nfsDebugStr[idx].equalsIgnoreCase(dbg) == false) - idx++; - - if (idx >= m_nfsDebugStr.length) - throw new AlfrescoRuntimeException("Invalid NFS debug flag, " + dbg); - - // Set the debug flag - - nfsDbg += 1 << idx; - } - - // Set the NFS debug flags - - nfsConfig.setNFSDebug(nfsDbg); - } - - // Check if mount server debug output is enabled - - if (nfsConfigBean.getMountServerDebug()) - nfsConfig.setMountServerDebug(true); - - // Check if portmapper debug output is enabled - - if (nfsConfigBean.getPortMapperDebug()) - nfsConfig.setPortMapperDebug(true); - - // Create the RPC authenticator - RpcAuthenticator rpcAuthenticator = nfsConfigBean.getRpcAuthenticator(); - if (rpcAuthenticator != null) - { - nfsConfig.setRpcAuthenticator(rpcAuthenticator); - } - else - throw new AlfrescoRuntimeException("RPC authenticator configuration missing, require user mappings"); - } - catch (InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException(ex.getMessage()); - } - } - - /** - * Process the filesystems configuration - */ - protected void processFilesystemsConfig() - { - // Create the filesystems configuration section - - FilesystemsConfigSection fsysConfig = new FilesystemsConfigSection(this); - - // Access the security configuration section - - SecurityConfigSection secConfig = (SecurityConfigSection) getConfigSection(SecurityConfigSection.SectionName); - - // Process the filesystems list - - if (this.filesystemContexts != null) - { - - // Add the filesystems - - for (DeviceContext filesystem : this.filesystemContexts) - { - - // Get the current filesystem configuration - - try - { - // Check the filesystem type and use the appropriate driver - - DiskSharedDevice filesys = null; - - if (filesystem instanceof AVMContext) - { - // Create a new filesystem driver instance and register a context for - // the new filesystem - - ExtendedDiskInterface filesysDriver = getAvmDiskInterface(); - DiskDeviceContext diskCtx = (DiskDeviceContext) filesystem; - - if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) - { - if(logger.isDebugEnabled()) - { - logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); - } - GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); - HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); - hazel.initializeCache(hazelConfig, this); - diskCtx.setStateCache(hazel); - } - else - { - // Check if the filesystem uses the file state cache, if so then add to the file state reaper - StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); - standaloneCache.initializeCache( new GenericConfigElement( ""), this); - diskCtx.setStateCache(standaloneCache); - } - if ( diskCtx.hasStateCache()) { - - // Register the state cache with the reaper thread - - fsysConfig.addFileStateCache( filesystem.getDeviceName(), diskCtx.getStateCache()); - } - - filesysDriver.registerContext(filesystem); - - // Create the shared filesystem - - filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, (AVMContext)filesystem); - filesys.setConfiguration( this); - // Start the filesystem - - ((AVMContext)filesystem).startFilesystem(filesys); - } - else - { - // Create a new filesystem driver instance and register a context for - // the new filesystem - - ExtendedDiskInterface filesysDriver = getRepoDiskInterface(); - ContentContext filesysContext = (ContentContext) filesystem; - - if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) - { - if(logger.isDebugEnabled()) - { - logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ filesysContext.getShareName()); - } - GenericConfigElement hazelConfig = createClusterConfig(filesysContext.getShareName()); - HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); - hazel.initializeCache(hazelConfig, this); - filesysContext.setStateCache(hazel); - } - else - { - // Create state cache here and inject - StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); - standaloneCache.initializeCache( new GenericConfigElement( ""), this); - filesysContext.setStateCache(standaloneCache); - } - - if ( filesysContext.hasStateCache()) { - - // Register the state cache with the reaper thread - - fsysConfig.addFileStateCache( filesystem.getDeviceName(), filesysContext.getStateCache()); - - // Create the lock manager for the context. - FileStateLockManager lockMgr = new FileStateLockManager(filesysContext.getStateCache()); - filesysContext.setLockManager(lockMgr); - filesysContext.setOpLockManager(lockMgr); - } - - filesysDriver.registerContext(filesystem); - - // Check if an access control list has been specified - - AccessControlList acls = null; - AccessControlListBean accessControls = filesysContext.getAccessControlList(); - if (accessControls != null) - { - // Parse the access control list - acls = accessControls.toAccessControlList(secConfig); - } - else if (secConfig.hasGlobalAccessControls()) - { - - // Use the global access control list for this disk share - acls = secConfig.getGlobalAccessControls(); - } - - // Create the shared filesystem - - filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, filesysContext); - filesys.setConfiguration( this); - - // Add any access controls to the share - - filesys.setAccessControlList(acls); - - - - // Check if change notifications should be enabled - - if ( filesysContext.getDisableChangeNotifications() == false) - filesysContext.enableChangeHandler( true); - - // Start the filesystem - - filesysContext.startFilesystem(filesys); - } - - // Add the new filesystem - - fsysConfig.addShare(filesys); - } - catch (DeviceContextException ex) - { - throw new AlfrescoRuntimeException("Error creating filesystem " + filesystem.getDeviceName(), ex); - } - catch (InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException(ex.getMessage(), ex); - } - } - } - else - { - // No filesystems defined - - logger.warn("No filesystems defined"); - } - - // Check if shares should be added for all AVM stores - if (this.avmAllStores && 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 (fsysConfig.getShares().findShare(storeName, ShareType.DISK, true) == null) - { - // Create the new share for the store - - AVMContext avmContext = new AVMContext(storeName, storeName + ":/", AVMContext.VERSION_HEAD); -// avmContext.enableStateCache(this, true); - - // Create the shared filesystem - - DiskSharedDevice filesys = new DiskSharedDevice(storeName, avmDriver, avmContext); - filesys.setConfiguration( this); - - fsysConfig.addShare( filesys); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("Added AVM share " + storeName); - } - } - } - } - - - // home folder share mapper could be declared in security config - } - - /** - * Process the security configuration - */ - protected void processSecurityConfig() - { - // Create the security configuration section - - SecurityConfigSection secConfig = new SecurityConfigSection(this); - - try - { - // Check if global access controls have been specified - - AccessControlListBean accessControls = securityConfigBean.getGlobalAccessControl(); - - if (accessControls != null) - { - // Parse the access control list - AccessControlList acls = accessControls.toAccessControlList(secConfig); - if (acls != null) - secConfig.setGlobalAccessControls(acls); - } - - - // Check if a JCE provider class has been specified - - String jceProvider = securityConfigBean.getJCEProvider(); - if (jceProvider != null && jceProvider.length() > 0) - { - - // Set the JCE provider - - secConfig.setJCEProvider(jceProvider); - } - else - { - // Use the default Bouncy Castle JCE provider - - secConfig.setJCEProvider("org.bouncycastle.jce.provider.BouncyCastleProvider"); - } - - // Check if a share mapper has been specified - - ShareMapper shareMapper = securityConfigBean.getShareMapper(); - if (shareMapper != null) - { - // Associate the share mapper - secConfig.setShareMapper(shareMapper); - } - else - { - // Check if the tenant service is enabled - if (m_tenantService != null && m_tenantService.isEnabled()) - { - // Initialize the multi-tenancy share mapper - - secConfig.setShareMapper("org.alfresco.filesys.alfresco.MultiTenantShareMapper", - new GenericConfigElement("shareMapper")); - - } - } - - // Check if any domain mappings have been specified - - List mappings = securityConfigBean.getDomainMappings(); - if (mappings != null) - { - DomainMapping mapping = null; - - for (DomainMappingConfigBean domainMap : mappings) - { - // Get the domain name - - String name = domainMap.getName(); - - // Check if the domain is specified by subnet or range - - String subnetStr = domainMap.getSubnet(); - String rangeFromStr; - if (subnetStr != null && subnetStr.length() > 0) - { - String maskStr = domainMap.getMask(); - - // Parse the subnet and mask, to validate and convert to int values - - int subnet = IPAddress.parseNumericAddress(subnetStr); - int mask = IPAddress.parseNumericAddress(maskStr); - - if (subnet == 0 || mask == 0) - throw new AlfrescoRuntimeException("Invalid subnet/mask for domain mapping " + name); - - // Create the subnet domain mapping - - mapping = new SubnetDomainMapping(name, subnet, mask); - } - else if ((rangeFromStr = domainMap.getRangeFrom()) != null && rangeFromStr.length() > 0) - { - String rangeToStr = domainMap.getRangeTo(); - - // Parse the range from/to values and convert to int values - - int rangeFrom = IPAddress.parseNumericAddress(rangeFromStr); - int rangeTo = IPAddress.parseNumericAddress(rangeToStr); - - if (rangeFrom == 0 || rangeTo == 0) - throw new AlfrescoRuntimeException("Invalid address range domain mapping " + name); - - // Create the subnet domain mapping - - mapping = new RangeDomainMapping(name, rangeFrom, rangeTo); - } - else - throw new AlfrescoRuntimeException("Invalid domain mapping specified"); - - // Add the domain mapping - - secConfig.addDomainMapping(mapping); - } - } - } - catch (InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException(ex.getMessage()); - } - } - - /** - * Process the core server configuration - * - * @exception InvalidConfigurationException - */ - protected void processCoreServerConfig() throws InvalidConfigurationException - { - // Create the core server configuration section - - CoreServerConfigSection coreConfig = new CoreServerConfigSection(this); - - // Check if the CIFS server is not enabled, do not create the thread/memory pools - - if ( cifsConfigBean == null || cifsConfigBean.getServerEnabled() == false) - return; - - // Check if the server core element has been specified - - if (coreServerConfigBean == null) - { - - // Configure a default memory pool - - coreConfig.setMemoryPool(DefaultMemoryPoolBufSizes, DefaultMemoryPoolInitAlloc, DefaultMemoryPoolMaxAlloc); - - // Configure a default thread pool size - - coreConfig.setThreadPool(DefaultThreadPoolInit, DefaultThreadPoolMax); - return; - } - - // Check if the thread pool size has been specified - - Integer initSize = coreServerConfigBean.getThreadPoolInit(); - if (initSize == null) - { - initSize = DefaultThreadPoolInit; - } - Integer maxSize = coreServerConfigBean.getThreadPoolMax(); - if (maxSize == null) - { - maxSize = DefaultThreadPoolMax; - } - - // Range check the thread pool size - - if (initSize < ThreadRequestPool.MinimumWorkerThreads) - throw new InvalidConfigurationException("Thread pool size below minimum allowed size"); - - if (initSize > ThreadRequestPool.MaximumWorkerThreads) - throw new InvalidConfigurationException("Thread pool size above maximum allowed size"); - - // Range check the maximum thread pool size - - if (maxSize < ThreadRequestPool.MinimumWorkerThreads) - throw new InvalidConfigurationException("Thread pool maximum size below minimum allowed size"); - - if (maxSize > ThreadRequestPool.MaximumWorkerThreads) - throw new InvalidConfigurationException("Thread pool maximum size above maximum allowed size"); - - if (maxSize < initSize) - throw new InvalidConfigurationException("Initial size is larger than maxmimum size"); - - // Configure the thread pool - - coreConfig.setThreadPool(initSize, maxSize); - - // Check if thread pool debug output is enabled - - if (coreServerConfigBean.getThreadPoolDebug()) - coreConfig.getThreadPool().setDebug(true); - - // Check if the packet sizes/allocations have been specified - - List packetSizes = coreServerConfigBean.getMemoryPacketSizes(); - if (packetSizes != null) - { - - // Calculate the array size for the packet size/allocation arrays - - int elemCnt = packetSizes.size(); - - // Create the packet size, initial allocation and maximum allocation arrays - - int[] pktSizes = new int[elemCnt]; - int[] initSizes = new int[elemCnt]; - int[] maxSizes = new int[elemCnt]; - - int elemIdx = 0; - - // Process the packet size elements - for (MemoryPacketConfigBean curChild : packetSizes) - { - - // Get the packet size - - int pktSize = -1; - - Long pktSizeLong = curChild.getSize(); - if (pktSizeLong == null) - throw new InvalidConfigurationException("Memory pool packet size not specified"); - - // Parse the packet size - - try - { - pktSize = MemorySize.getByteValueInt(pktSizeLong.toString()); - } - catch (NumberFormatException ex) - { - throw new InvalidConfigurationException("Memory pool packet size, invalid size value, " - + pktSizeLong); - } - - // Make sure the packet sizes have been specified in ascending order - - if (elemIdx > 0 && pktSizes[elemIdx - 1] >= pktSize) - throw new InvalidConfigurationException( - "Invalid packet size specified, less than/equal to previous packet size"); - - // Get the initial allocation for the current packet size - Integer initAlloc = curChild.getInit(); - if (initAlloc == null) - throw new InvalidConfigurationException("Memory pool initial allocation not specified"); - - // Range check the initial allocation - - if (initAlloc < MemoryPoolMinimumAllocation) - throw new InvalidConfigurationException("Initial memory pool allocation below minimum of " - + MemoryPoolMinimumAllocation); - - if (initAlloc > MemoryPoolMaximumAllocation) - throw new InvalidConfigurationException("Initial memory pool allocation above maximum of " - + MemoryPoolMaximumAllocation); - - // Get the maximum allocation for the current packet size - - Integer maxAlloc = curChild.getMax(); - if (maxAlloc == null) - throw new InvalidConfigurationException("Memory pool maximum allocation not specified"); - - // Range check the maximum allocation - - if (maxAlloc < MemoryPoolMinimumAllocation) - throw new InvalidConfigurationException("Maximum memory pool allocation below minimum of " - + MemoryPoolMinimumAllocation); - - if (initAlloc > MemoryPoolMaximumAllocation) - throw new InvalidConfigurationException("Maximum memory pool allocation above maximum of " - + MemoryPoolMaximumAllocation); - - // Set the current packet size elements - - pktSizes[elemIdx] = pktSize; - initSizes[elemIdx] = initAlloc; - maxSizes[elemIdx] = maxAlloc; - - elemIdx++; - } - - // Check if all elements were used in the packet size/allocation arrays - - if (elemIdx < pktSizes.length) - { - - // Re-allocate the packet size/allocation arrays - - int[] newPktSizes = new int[elemIdx]; - int[] newInitSizes = new int[elemIdx]; - int[] newMaxSizes = new int[elemIdx]; - - // Copy the values to the shorter arrays - - System.arraycopy(pktSizes, 0, newPktSizes, 0, elemIdx); - System.arraycopy(initSizes, 0, newInitSizes, 0, elemIdx); - System.arraycopy(maxSizes, 0, newMaxSizes, 0, elemIdx); - - // Move the new arrays into place - - pktSizes = newPktSizes; - initSizes = newInitSizes; - maxSizes = newMaxSizes; - } - - // Configure the memory pool - - coreConfig.setMemoryPool(pktSizes, initSizes, maxSizes); - } - else - { - - // Configure a default memory pool - - coreConfig.setMemoryPool(DefaultMemoryPoolBufSizes, DefaultMemoryPoolInitAlloc, DefaultMemoryPoolMaxAlloc); - } - } - - /** - * Initialise a runtime context - not configured through spring e.g MT. - * - * TODO - what about desktop actions etc? - * - * @param diskCtx - */ - public void initialiseRuntimeContext(AlfrescoContext diskCtx) - { - if (diskCtx.getStateCache() == null) { - - // Set the state cache, use a hard coded standalone cache for now - FilesystemsConfigSection filesysConfig = (FilesystemsConfigSection) this.getConfigSection( FilesystemsConfigSection.SectionName); - - if ( filesysConfig != null) - { - - try - { - if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) - { - if(logger.isDebugEnabled()) - { - logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); - } - GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); - HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); - hazel.initializeCache(hazelConfig, this); - diskCtx.setStateCache(hazel); - } - else - { - // Create a standalone state cache - StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); - standaloneCache.initializeCache( new GenericConfigElement( ""), this); - filesysConfig.addFileStateCache( diskCtx.getDeviceName(), standaloneCache); - diskCtx.setStateCache( standaloneCache); - } - - FileStateLockManager lockMgr = new FileStateLockManager(diskCtx.getStateCache()); - diskCtx.setLockManager(lockMgr); - diskCtx.setOpLockManager(lockMgr); - } - catch ( InvalidConfigurationException ex) - { - throw new AlfrescoRuntimeException( "Failed to initialize standalone state cache for " + diskCtx.getDeviceName()); - } - } - } - } - - - @Override - protected void processClusterConfig() throws InvalidConfigurationException - { - -// Done by org.alfresco.jlan.server.config.ServerConfiguration.closeConfiguration -// /** -// * Close the old hazelcast configuration -// */ -// ClusterConfigSection secConfig = (ClusterConfigSection) getConfigSection(ClusterConfigSection.SectionName); -// { -// if(secConfig != null) -// { -// secConfig.closeConfig(); -// } -// } - - if (clusterConfigBean == null || !clusterConfigBean.getClusterEnabled()) - { - removeConfigSection(ClusterConfigSection.SectionName); - logger.info("Filesystem cluster cache not enabled"); - return; - } - - String clusterName = clusterConfigBean.getClusterName(); - if (clusterName == null || clusterName.length() == 0) - { - throw new InvalidConfigurationException("Cluster name not specified or invalid"); - } - - String clusterFile = clusterConfigBean.getConfigFile(); - if (clusterFile == null || clusterFile.length() == 0) - { - throw new InvalidConfigurationException("Cluster config file not specified or invalid"); - } - - // New Hazelcast instance created here within the ClusterConfigSection + + /** + * Default constructor + */ + public ServerConfigurationBean() + { + super(""); + } + + /** + * Class constructor + * + * @param srvName + * String + */ + public ServerConfigurationBean(String srvName) + { + super(srvName); + } + + public void setCifsConfigBean(CIFSConfigBean cifsConfigBean) + { + this.cifsConfigBean = cifsConfigBean; + } + + public void setFtpConfigBean(FTPConfigBean ftpConfigBean) + { + this.ftpConfigBean = ftpConfigBean; + } + + public void setNfsConfigBean(NFSConfigBean nfsConfigBean) + { + this.nfsConfigBean = nfsConfigBean; + } + + public void setFilesystemContexts(List filesystemContexts) + { + this.filesystemContexts = filesystemContexts; + } + + public void setAvmAllStores(boolean avmAllStores) + { + this.avmAllStores = avmAllStores; + } + + public void setSecurityConfigBean(SecurityConfigBean securityConfigBean) + { + this.securityConfigBean = securityConfigBean; + } + + public void setCoreServerConfigBean(CoreServerConfigBean coreServerConfigBean) + { + this.coreServerConfigBean = coreServerConfigBean; + } + + public void setClusterConfigBean(ClusterConfigBean clusterConfigBean) + { + this.clusterConfigBean = clusterConfigBean; + } + + /** + * Process the CIFS server configuration + */ + protected void processCIFSServerConfig() + { + // If the configuration section is not valid then CIFS is disabled + + if (cifsConfigBean == null) + { + removeConfigSection(CIFSConfigSection.SectionName); + return; + } + + // Check if the server has been disabled + if (!cifsConfigBean.getServerEnabled()) + { + removeConfigSection(CIFSConfigSection.SectionName); + return; + } + + // Before we go any further, let's make sure there's a compatible authenticator in the authentication chain. + ICifsAuthenticator authenticator = cifsConfigBean.getAuthenticator(); + if (authenticator == null || authenticator instanceof ActivateableBean && !((ActivateableBean)authenticator).isActive()) + { + logger.warn("No enabled CIFS authenticator found in authentication chain. CIFS Server disabled"); + removeConfigSection(CIFSConfigSection.SectionName); + return; + } + + // Create the CIFS server configuration section + + CIFSConfigSection cifsConfig = new CIFSConfigSection(this); + + try + { + // Check if native code calls should be disabled on Windows + if (cifsConfigBean.getDisableNativeCode()) + { + // Disable native code calls so that the JNI DLL is not required + + cifsConfig.setNativeCodeDisabled(true); + m_disableNativeCode = true; + + // Warning + + logger.warn("CIFS server native calls disabled, JNI code will not be used"); + } + + // Get the network broadcast address + // + // Note: We need to set this first as the call to getLocalDomainName() may use a NetBIOS + // name lookup, so the broadcast mask must be set before then. + + String broadcastAddess = cifsConfigBean.getBroadcastAddress(); + if (broadcastAddess != null && broadcastAddess.length() > 0) + { + + // Check if the broadcast mask is a valid numeric IP address + + if (IPAddress.isNumericAddress(broadcastAddess) == false) + throw new AlfrescoRuntimeException("Invalid broadcast mask, must be n.n.n.n format"); + + // Set the network broadcast mask + + cifsConfig.setBroadcastMask(broadcastAddess); + } + + // Get the host configuration + + String hostName = cifsConfigBean.getServerName(); + if (hostName == null || hostName.length() == 0) + throw new AlfrescoRuntimeException("Host name not specified or invalid"); + + // Check if the host name contains the local name token + + int pos = hostName.indexOf(TokenLocalName); + if (pos != -1) + { + + // Get the local server name + + String srvName = getLocalServerName(true); + + // Rebuild the host name substituting the token with the local server name + + StringBuilder hostStr = new StringBuilder(); + + hostStr.append(hostName.substring(0, pos)); + hostStr.append(srvName); + + pos += TokenLocalName.length(); + if (pos < hostName.length()) + hostStr.append(hostName.substring(pos)); + + hostName = hostStr.toString(); + + // Make sure the CIFS server name does not match the local server name + + if (hostName.equals(srvName) && getPlatformType() == Platform.Type.WINDOWS) + throw new AlfrescoRuntimeException("CIFS server name must be unique"); + } + + // Check if the host name is longer than 15 characters. NetBIOS only allows a maximum of 16 characters in + // the + // server name with the last character reserved for the service type. + + if (hostName.length() > 15) + { + // Truncate the CIFS server name + + hostName = hostName.substring(0, 15); + + // Output a warning + + logger.warn("CIFS server name is longer than 15 characters, truncated to " + hostName); + } + + // Set the CIFS server name + + cifsConfig.setServerName(hostName.toUpperCase()); + setServerName(hostName.toUpperCase()); + + // Get the domain/workgroup name + + String domain = cifsConfigBean.getDomainName(); + if (domain != null && domain.length() > 0) + { + // Set the domain/workgroup name + + cifsConfig.setDomainName(domain.toUpperCase()); + } + else + { + // Get the local domain/workgroup name + + String localDomain = getLocalDomainName(); + + if (localDomain == null && (getPlatformType() != Platform.Type.WINDOWS || isNativeCodeDisabled())) + { + // Use a default domain/workgroup name + + localDomain = "WORKGROUP"; + + // Output a warning + + logger.error("Failed to get local domain/workgroup name, using default of " + localDomain); + logger.error("(This may be due to firewall settings or incorrect setting)"); + } + + // Set the local domain/workgroup that the CIFS server belongs to + + cifsConfig.setDomainName(localDomain); + } + + // Check for a server comment + String comment = cifsConfigBean.getServerComment(); + if (comment != null && comment.length() > 0) + { + cifsConfig.setComment(comment); + } + + // Check for a bind address + + // Check if the network adapter name has been specified + String bindToAdapter = cifsConfigBean.getBindToAdapter(); + String bindTo; + + if (bindToAdapter != null && bindToAdapter.length() > 0) + { + + // Get the IP address for the adapter + + InetAddress bindAddr = parseAdapterName(bindToAdapter); + + // Set the bind address for the server + + cifsConfig.setSMBBindAddress(bindAddr); + } + else if ((bindTo = cifsConfigBean.getBindToAddress()) != null && bindTo.length() > 0 + && !bindTo.equals(BIND_TO_IGNORE)) + { + + // Validate the bind address + try + { + // Check the bind address + + InetAddress bindAddr = InetAddress.getByName(bindTo); + + // Set the bind address for the server + + cifsConfig.setSMBBindAddress(bindAddr); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid CIFS server bind address"); + } + } + + // Get the authenticator + + if (authenticator != null) + { + cifsConfig.setAuthenticator(authenticator); + } + else + throw new AlfrescoRuntimeException("CIFS authenticator not specified"); + + // Check if the host announcer has been disabled + + if (!cifsConfigBean.getHostAccouncerEnabled()) + { + // Switch off the host announcer + + cifsConfig.setHostAnnouncer( false); + + // Log that host announcements are not enabled + + logger.info("Host announcements not enabled"); + } + else + { + // Check for an announcement interval + + Integer interval = cifsConfigBean.getHostAccounceInterval(); + if (interval != null) + { + cifsConfig.setHostAnnounceInterval(interval); + } + + // Check if the domain name has been set, this is required if the + // host announcer is enabled + + if (cifsConfig.getDomainName() == null) + throw new AlfrescoRuntimeException("Domain name must be specified if host announcement is enabled"); + + // Enable host announcement + + cifsConfig.setHostAnnouncer(true); + } + + // Check if NetBIOS SMB is enabled + NetBIOSSMBConfigBean netBIOSSMBConfigBean = cifsConfigBean.getNetBIOSSMB(); + if (netBIOSSMBConfigBean != null) + { + // Check if NetBIOS over TCP/IP is enabled for the current platform + + String platformsStr = netBIOSSMBConfigBean.getPlatforms(); + boolean platformOK = false; + + if (platformsStr != null && platformsStr.length() > 0) + { + // Parse the list of platforms that NetBIOS over TCP/IP is to be enabled for and + // check if the current platform is included + + EnumSet enabledPlatforms = parsePlatformString(platformsStr); + if (enabledPlatforms.contains(getPlatformType())) + platformOK = true; + } + else + { + // No restriction on platforms + + platformOK = true; + } + + // Enable the NetBIOS SMB support, if enabled for this platform + + cifsConfig.setNetBIOSSMB(platformOK); + + // Parse/check NetBIOS settings, if enabled + + if (cifsConfig.hasNetBIOSSMB()) + { + // Check if the broadcast mask has been specified + + if (cifsConfig.getBroadcastMask() == null) + throw new AlfrescoRuntimeException("Network broadcast mask not specified"); + + // Check for a bind address + + String bindto = netBIOSSMBConfigBean.getBindTo(); + if (bindto != null && bindto.length() > 0 && !bindto.equals(BIND_TO_IGNORE)) + { + + // Validate the bind address + + try + { + + // Check the bind address + + InetAddress bindAddr = InetAddress.getByName(bindto); + + // Set the bind address for the NetBIOS name server + + cifsConfig.setNetBIOSBindAddress(bindAddr); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid NetBIOS bind address"); + } + } + else if (cifsConfig.hasSMBBindAddress()) + { + + // Use the SMB bind address for the NetBIOS name server + + cifsConfig.setNetBIOSBindAddress(cifsConfig.getSMBBindAddress()); + } + else + { + // Get a list of all the local addresses + + InetAddress[] addrs = null; + + try + { + // Get the local server IP address list + + addrs = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName()); + } + catch (UnknownHostException ex) + { + logger.error("Failed to get local address list", ex); + } + + // Check the address list for one or more valid local addresses filtering out the loopback + // address + + int addrCnt = 0; + + if (addrs != null) + { + for (int i = 0; i < addrs.length; i++) + { + + // Check for a valid address, filter out '127.0.0.1' and '0.0.0.0' addresses + + if (addrs[i].getHostAddress().equals("127.0.0.1") == false + && addrs[i].getHostAddress().equals("0.0.0.0") == false) + addrCnt++; + } + } + + // Check if any addresses were found + + if (addrCnt == 0) + { + // Enumerate the network adapter list + + Enumeration niEnum = null; + + try + { + niEnum = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException ex) + { + } + + if (niEnum != null) + { + while (niEnum.hasMoreElements()) + { + // Get the current network interface + + NetworkInterface ni = niEnum.nextElement(); + + // Enumerate the addresses for the network adapter + + Enumeration niAddrs = ni.getInetAddresses(); + if (niAddrs != null) + { + // Check for any valid addresses + + while (niAddrs.hasMoreElements()) + { + InetAddress curAddr = niAddrs.nextElement(); + + if (curAddr.getHostAddress().equals("127.0.0.1") == false + && curAddr.getHostAddress().equals("0.0.0.0") == false) + addrCnt++; + } + } + } + + // DEBUG + + if (addrCnt > 0 && logger.isDebugEnabled()) + logger.debug("Found valid IP address from interface list"); + } + + // Check if we found any valid network addresses + + if (addrCnt == 0) + { + // Log the available IP addresses + + if (logger.isDebugEnabled()) + { + logger.debug("Local address list dump :-"); + if (addrs != null) + { + for (int i = 0; i < addrs.length; i++) + logger.debug(" Address: " + addrs[i]); + } + else + logger.debug(" No addresses"); + } + + // Throw an exception to stop the CIFS/NetBIOS name server from starting + + throw new AlfrescoRuntimeException( + "Failed to get IP address(es) for the local server, check hosts file and/or DNS setup"); + } + } + } + + // Check if the session port has been specified + + Integer portNum = netBIOSSMBConfigBean.getSessionPort(); + if (portNum != null) + { + cifsConfig.setSessionPort(portNum); + if (cifsConfig.getSessionPort() <= 0 || cifsConfig.getSessionPort() >= 65535) + throw new AlfrescoRuntimeException("NetBIOS session port out of valid range"); + } + + // Check if the name port has been specified + + portNum = netBIOSSMBConfigBean.getNamePort(); + if (portNum != null) + { + cifsConfig.setNameServerPort(portNum); + if (cifsConfig.getNameServerPort() <= 0 || cifsConfig.getNameServerPort() >= 65535) + throw new AlfrescoRuntimeException("NetBIOS name port out of valid range"); + } + + // Check if the datagram port has been specified + + portNum = netBIOSSMBConfigBean.getDatagramPort(); + if (portNum != null) + { + cifsConfig.setDatagramPort(portNum); + if (cifsConfig.getDatagramPort() <= 0 || cifsConfig.getDatagramPort() >= 65535) + throw new AlfrescoRuntimeException("NetBIOS datagram port out of valid range"); + } + + // Check for a bind address + + String attr = netBIOSSMBConfigBean.getBindTo(); + if (attr != null && attr.length() > 0 && !attr.equals(BIND_TO_IGNORE)) + { + + // Validate the bind address + + try + { + + // Check the bind address + + InetAddress bindAddr = InetAddress.getByName(attr); + + // Set the bind address for the NetBIOS name server + + cifsConfig.setNetBIOSBindAddress(bindAddr); + } + catch (UnknownHostException ex) + { + throw new InvalidConfigurationException(ex.toString()); + } + } + + // Check for a bind address using the adapter name + + else if ((attr = netBIOSSMBConfigBean.getAdapter()) != null && attr.length() > 0) + { + + // Get the bind address via the network adapter name + + InetAddress bindAddr = parseAdapterName(attr); + cifsConfig.setNetBIOSBindAddress(bindAddr); + } + else if (cifsConfig.hasSMBBindAddress()) + { + + // Use the SMB bind address for the NetBIOS name server + + cifsConfig.setNetBIOSBindAddress(cifsConfig.getSMBBindAddress()); + } + + } + } + else + { + + // Disable NetBIOS SMB support + + cifsConfig.setNetBIOSSMB(false); + } + + // Check if TCP/IP SMB is enabled + + TcpipSMBConfigBean tcpipSMBConfigBean = cifsConfigBean.getTcpipSMB(); + if (tcpipSMBConfigBean != null) + { + + // Check if native SMB is enabled for the current platform + + String platformsStr = tcpipSMBConfigBean.getPlatforms(); + boolean platformOK = false; + + if (platformsStr != null) + { + // Parse the list of platforms that native SMB is to be enabled for and + // check if the current platform is included + + EnumSet enabledPlatforms = parsePlatformString(platformsStr); + if (enabledPlatforms.contains(getPlatformType())) + platformOK = true; + } + else + { + // No restriction on platforms + + platformOK = true; + } + + // Enable the TCP/IP SMB support, if enabled for this platform + + cifsConfig.setTcpipSMB(platformOK); + + // Check if the port has been specified + + Integer portNum = tcpipSMBConfigBean.getPort(); + if (portNum != null) + { + cifsConfig.setTcpipSMBPort(portNum); + if (cifsConfig.getTcpipSMBPort() <= 0 || cifsConfig.getTcpipSMBPort() >= 65535) + throw new AlfrescoRuntimeException("TCP/IP SMB port out of valid range"); + } + + // Check if IPv6 support should be enabled + + if ( tcpipSMBConfigBean.getIpv6Enabled()) + { + try + { + // Use the IPv6 bind all address + + cifsConfig.setSMBBindAddress( InetAddress.getByName( "::")); + + // DEBUG + + if ( logger.isInfoEnabled()) + logger.info("Enabled CIFS IPv6 bind address for native SMB"); + } + catch ( UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Failed to enable IPv6 bind address, " + ex.getMessage()); + } + } + + } + else + { + + // Disable TCP/IP SMB support + + cifsConfig.setTcpipSMB(false); + } + + // Check if Win32 NetBIOS is enabled + + Win32NetBIOSConfigBean win32NetBIOSConfigBean = cifsConfigBean.getWin32NetBIOS(); + if (win32NetBIOSConfigBean != null) + { + + // Check if the Win32 NetBIOS server name has been specified + + String win32Name = win32NetBIOSConfigBean.getName(); + if (win32Name != null && win32Name.length() > 0) + { + + // Validate the name + + if (win32Name.length() > 16) + throw new AlfrescoRuntimeException("Invalid Win32 NetBIOS name, " + win32Name); + + // Set the Win32 NetBIOS file server name + + cifsConfig.setWin32NetBIOSName(win32Name); + } + + // Check if the Win32 NetBIOS LANA has been specified + + String lanaStr = win32NetBIOSConfigBean.getLana(); + if (lanaStr != null && lanaStr.length() > 0) + { + // Check if the LANA has been specified as an IP address or adapter name + + int lana = -1; + + if (IPAddress.isNumericAddress(lanaStr)) + { + + // Convert the IP address to a LANA id + + lana = Win32NetBIOS.getLANAForIPAddress(lanaStr); + if (lana == -1) + throw new AlfrescoRuntimeException("Failed to convert IP address " + lanaStr + " to a LANA"); + } + else if (lanaStr.length() > 1 && Character.isLetter(lanaStr.charAt(0))) + { + + // Convert the network adapter to a LANA id + + lana = Win32NetBIOS.getLANAForAdapterName(lanaStr); + if (lana == -1) + throw new AlfrescoRuntimeException("Failed to convert network adapter " + lanaStr + + " to a LANA"); + } + else + { + + try + { + lana = Integer.parseInt(lanaStr); + } + catch (NumberFormatException ex) + { + throw new AlfrescoRuntimeException("Invalid win32 NetBIOS LANA specified"); + } + } + + // LANA should be in the range 0-255 + + if (lana < 0 || lana > 255) + throw new AlfrescoRuntimeException("Invalid Win32 NetBIOS LANA number, " + lana); + + // Set the LANA number + + cifsConfig.setWin32LANA(lana); + } + + // Check if the native NetBIOS interface has been specified, either 'winsock' or 'netbios' + + String nativeAPI = win32NetBIOSConfigBean.getApi(); + if (nativeAPI != null && nativeAPI.length() > 0) + { + // Validate the API type + + boolean useWinsock = true; + + if (nativeAPI.equalsIgnoreCase("netbios")) + useWinsock = false; + else if (nativeAPI.equalsIgnoreCase("winsock") == false) + throw new AlfrescoRuntimeException("Invalid NetBIOS API type, spefify 'winsock' or 'netbios'"); + + // Set the NetBIOS API to use + + cifsConfig.setWin32WinsockNetBIOS(useWinsock); + } + + // Force the older NetBIOS API code to be used on 64Bit Windows + + if (cifsConfig.useWinsockNetBIOS() == true && X64.isWindows64()) + { + // Debug + + if (logger.isDebugEnabled()) + logger.debug("Using older Netbios() API code"); + + // Use the older NetBIOS API code + + cifsConfig.setWin32WinsockNetBIOS(false); + } + + // Check if the current operating system is supported by the Win32 + // NetBIOS handler + + String osName = System.getProperty("os.name"); + if (osName.startsWith("Windows") + && (osName.endsWith("95") == false && osName.endsWith("98") == false && osName.endsWith("ME") == false) + && isNativeCodeDisabled() == false) + { + + // Call the Win32NetBIOS native code to make sure it is initialized + + if (Win32NetBIOS.LanaEnumerate() != null) + { + // Enable Win32 NetBIOS + + cifsConfig.setWin32NetBIOS(true); + } + else + { + logger.warn("No NetBIOS LANAs available"); + } + } + else + { + + // Win32 NetBIOS not supported on the current operating system + + cifsConfig.setWin32NetBIOS(false); + } + } + else + { + + // Disable Win32 NetBIOS + + cifsConfig.setWin32NetBIOS(false); + } + + // Check if the Win32 host announcer has been disabled + + if ( !cifsConfigBean.getWin32HostAnnouncerEnabled()) + { + // Switch off the Win32 host announcer + + cifsConfig.setWin32HostAnnouncer( false); + + // Log that host announcements are not enabled + + logger.info("Win32 host announcements not enabled"); + } + else + { + // Check for an announcement interval + Integer interval = cifsConfigBean.getWin32HostAnnounceInterval(); + if (interval != null) + { + cifsConfig.setWin32HostAnnounceInterval(interval); + } + + // Check if the domain name has been set, this is required if the + // host announcer is enabled + + if (cifsConfig.getDomainName() == null) + throw new AlfrescoRuntimeException("Domain name must be specified if host announcement is enabled"); + + // Enable Win32 NetBIOS host announcement + + cifsConfig.setWin32HostAnnouncer(true); + } + + // Check if NetBIOS and/or TCP/IP SMB have been enabled + + if (cifsConfig.hasNetBIOSSMB() == false && cifsConfig.hasTcpipSMB() == false + && cifsConfig.hasWin32NetBIOS() == false) + throw new AlfrescoRuntimeException("NetBIOS SMB, TCP/IP SMB or Win32 NetBIOS must be enabled"); + + // Check if WINS servers are configured + + WINSConfigBean winsConfigBean = cifsConfigBean.getWINSConfig(); + + if (winsConfigBean != null && !winsConfigBean.isAutoDetectEnabled()) + { + + // Get the primary WINS server + + String priWins = winsConfigBean.getPrimary(); + + if (priWins == null || priWins.length() == 0) + throw new AlfrescoRuntimeException("No primary WINS server configured"); + + // Validate the WINS server address + + InetAddress primaryWINS = null; + + try + { + primaryWINS = InetAddress.getByName(priWins); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid primary WINS server address, " + priWins); + } + + // Check if a secondary WINS server has been specified + + String secWins = winsConfigBean.getSecondary(); + InetAddress secondaryWINS = null; + + if (secWins != null && secWins.length() > 0) + { + + // Validate the secondary WINS server address + + try + { + secondaryWINS = InetAddress.getByName(secWins); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid secondary WINS server address, " + secWins); + } + } + + // Set the WINS server address(es) + + cifsConfig.setPrimaryWINSServer(primaryWINS); + if (secondaryWINS != null) + cifsConfig.setSecondaryWINSServer(secondaryWINS); + + // Pass the setting to the NetBIOS session class + + NetBIOSSession.setDefaultWINSServer(primaryWINS); + } + + // Check if WINS is configured, if we are running on Windows and socket based NetBIOS is enabled + + else if (cifsConfig.hasNetBIOSSMB() && getPlatformType() == Platform.Type.WINDOWS && !isNativeCodeDisabled()) + { + // Get the WINS server list + + String winsServers = Win32NetBIOS.getWINSServerList(); + + if (winsServers != null) + { + // Use the first WINS server address for now + + StringTokenizer tokens = new StringTokenizer(winsServers, ","); + String addr = tokens.nextToken(); + + try + { + // Convert to a network address and check if the WINS server is accessible + + InetAddress winsAddr = InetAddress.getByName(addr); + + Socket winsSocket = new Socket(); + InetSocketAddress sockAddr = new InetSocketAddress(winsAddr, RFCNetBIOSProtocol.NAME_PORT); + + winsSocket.connect(sockAddr, 3000); + winsSocket.close(); + + // Set the primary WINS server address + + cifsConfig.setPrimaryWINSServer(winsAddr); + + // Debug + + if (logger.isDebugEnabled()) + logger.debug("Configuring to use WINS server " + addr); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid auto WINS server address, " + addr); + } + catch (IOException ex) + { + if (logger.isDebugEnabled()) + logger.debug("Failed to connect to auto WINS server " + addr); + } + } + } + + // Check for session debug flags + + String flags = cifsConfigBean.getSessionDebugFlags(); + + int sessDbg = 0; + + if (flags != null && flags.length() > 0) + { + + // Parse the flags + + flags = flags.toUpperCase(); + StringTokenizer token = new StringTokenizer(flags, ","); + + while (token.hasMoreTokens()) + { + // Get the current debug flag token + + String dbg = token.nextToken().trim(); + + // Find the debug flag name + + int idx = 0; + + while (idx < m_sessDbgStr.length && m_sessDbgStr[idx].equalsIgnoreCase(dbg) == false) + idx++; + + if (idx > m_sessDbgStr.length) + throw new AlfrescoRuntimeException("Invalid session debug flag, " + dbg); + + // Set the debug flag + + sessDbg += 1 << idx; + } + } + + // Set the session debug flags + + cifsConfig.setSessionDebugFlags(sessDbg); + + // Check if NIO based socket code should be disabled + + if (cifsConfigBean.getDisableNIO()) + { + + // Disable NIO based code + + cifsConfig.setDisableNIOCode(true); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("NIO based code disabled for CIFS server"); + } + + + // Check if a session timeout is configured + + Integer tmo = cifsConfigBean.getSessionTimeout(); + if (tmo != null) + { + + // Validate the session timeout value + + if (tmo < 0 || tmo > MaxSessionTimeout) + throw new AlfrescoRuntimeException("Session timeout out of range (0 - " + MaxSessionTimeout + ")"); + + // Convert the session timeout to milliseconds + + cifsConfig.setSocketTimeout(tmo * 1000); + } + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException(ex.getMessage()); + } + } + + /** + * Process the FTP server configuration + */ + protected void processFTPServerConfig() + { + // If the configuration section is not valid then FTP is disabled + + if (ftpConfigBean == null) + { + removeConfigSection(FTPConfigSection.SectionName); + return; + } + + // Check if the server has been disabled + + if (!ftpConfigBean.getServerEnabled()) + { + removeConfigSection(FTPConfigSection.SectionName); + return; + } + + // Create the FTP configuration section + + FTPConfigSection ftpConfig = new FTPConfigSection(this); + + try + { + // Check for a bind address + + String bindText = ftpConfigBean.getBindTo(); + if (bindText != null && bindText.length() > 0 && !bindText.equals(BIND_TO_IGNORE)) + { + + // Validate the bind address + + try + { + + // Check the bind address + + InetAddress bindAddr = InetAddress.getByName(bindText); + + // Set the bind address for the FTP server + + ftpConfig.setFTPBindAddress(bindAddr); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid FTP bindto address, " + bindText); + } + } + + // Check for an FTP server port + + Integer port = ftpConfigBean.getPort(); + if (port != null) + { + ftpConfig.setFTPPort(port); + if (ftpConfig.getFTPPort() <= 0 || ftpConfig.getFTPPort() >= 65535) + throw new AlfrescoRuntimeException("FTP server port out of valid range"); + } + else + { + + // Use the default FTP port + + ftpConfig.setFTPPort(DefaultFTPServerPort); + } + + // Check if anonymous login is allowed + + if (ftpConfigBean.getAllowAnonymous()) + { + + // Enable anonymous login to the FTP server + + ftpConfig.setAllowAnonymousFTP(true); + + // Check if an anonymous account has been specified + + String anonAcc = ftpConfigBean.getAnonymousAccount(); + if (anonAcc != null && anonAcc.length() > 0) + { + + // Set the anonymous account name + + ftpConfig.setAnonymousFTPAccount(anonAcc); + + // Check if the anonymous account name is valid + + if (ftpConfig.getAnonymousFTPAccount() == null || ftpConfig.getAnonymousFTPAccount().length() == 0) + throw new AlfrescoRuntimeException("Anonymous FTP account invalid"); + } + else + { + + // Use the default anonymous account name + + ftpConfig.setAnonymousFTPAccount(DefaultFTPAnonymousAccount); + } + } + else + { + + // Disable anonymous logins + + ftpConfig.setAllowAnonymousFTP(false); + } + + // Check if a root path has been specified + + String rootPath = ftpConfigBean.getRootDirectory(); + if (rootPath != null && rootPath.length() > 0) + { + try + { + + // Parse the path + + new FTPPath(rootPath); + + // Set the root path + + ftpConfig.setFTPRootPath(rootPath); + } + catch (InvalidPathException ex) + { + throw new AlfrescoRuntimeException("Invalid FTP root directory, " + rootPath); + } + } + + // Check for FTP debug flags + + String flags = ftpConfigBean.getDebugFlags(); + int ftpDbg = 0; + + if (flags != null) + { + + // Parse the flags + + flags = flags.toUpperCase(); + StringTokenizer token = new StringTokenizer(flags, ","); + + while (token.hasMoreTokens()) + { + + // Get the current debug flag token + + String dbg = token.nextToken().trim(); + + // Find the debug flag name + + int idx = 0; + + while (idx < m_ftpDebugStr.length && m_ftpDebugStr[idx].equalsIgnoreCase(dbg) == false) + idx++; + + if (idx >= m_ftpDebugStr.length) + throw new AlfrescoRuntimeException("Invalid FTP debug flag, " + dbg); + + // Set the debug flag + + ftpDbg += 1 << idx; + } + + // Set the FTP debug flags + + ftpConfig.setFTPDebug(ftpDbg); + } + + // Check if a character set has been specified + + String charSet = ftpConfigBean.getCharSet(); + if (charSet != null && charSet.length() > 0) + { + + try + { + + // Validate the character set name + + Charset.forName(charSet); + + // Set the FTP character set + + ftpConfig.setFTPCharacterSet(charSet); + } + catch (IllegalCharsetNameException ex) + { + throw new AlfrescoRuntimeException("Illegal character set name, " + charSet); + } + catch (UnsupportedCharsetException ex) + { + throw new AlfrescoRuntimeException("Unsupported character set name, " + charSet); + } + } + + // Check if an authenticator has been specified + + FTPAuthenticator auth = ftpConfigBean.getAuthenticator(); + if (auth != null) + { + + // Initialize and set the authenticator class + + ftpConfig.setAuthenticator(auth); + } + else + throw new AlfrescoRuntimeException("FTP authenticator not specified"); + + // Check if a data port range has been specified + + if ( ftpConfigBean.getDataPortFrom() != 0 && ftpConfigBean.getDataPortTo() != 0) { + + // Range check the data port values + + int rangeFrom = ftpConfigBean.getDataPortFrom(); + int rangeTo = ftpConfigBean.getDataPortTo(); + + if ( rangeFrom != 0 && rangeTo != 0) { + + // Validate the FTP data port range + + if ( rangeFrom < 1024 || rangeFrom > 65535) + throw new InvalidConfigurationException("Invalid FTP data port range from value, " + rangeFrom); + + if ( rangeTo < 1024 || rangeTo > 65535) + throw new InvalidConfigurationException("Invalid FTP data port range to value, " + rangeTo); + + if ( rangeFrom >= rangeTo) + throw new InvalidConfigurationException("Invalid FTP data port range, " + rangeFrom + "-" + rangeTo); + + // Set the FTP data port range + + ftpConfig.setFTPDataPortLow(rangeFrom); + ftpConfig.setFTPDataPortHigh(rangeTo); + + // Log the data port range + + logger.info("FTP server data ports restricted to range " + rangeFrom + ":" + rangeTo); + } + } + + // FTPS parameter parsing + // + // Check if a key store path has been specified + + if ( ftpConfigBean.getKeyStorePath() != null && ftpConfigBean.getKeyStorePath().length() > 0) { + + // Get the path to the key store, check that the file exists + + String keyStorePath = ftpConfigBean.getKeyStorePath(); + File keyStoreFile = new File( keyStorePath); + + if ( keyStoreFile.exists() == false) + throw new InvalidConfigurationException("FTPS key store file does not exist, " + keyStorePath); + else if ( keyStoreFile.isDirectory()) + throw new InvalidConfigurationException("FTPS key store path is a directory, " + keyStorePath); + + // Set the key store path + + ftpConfig.setKeyStorePath( keyStorePath); + } + + // Check if the trust store path has been specified + + if ( ftpConfigBean.getTrustStorePath() != null && ftpConfigBean.getTrustStorePath().length() > 0) { + + // Get the path to the trust store, check that the file exists + + String trustStorePath = ftpConfigBean.getTrustStorePath(); + File trustStoreFile = new File( trustStorePath); + + if ( trustStoreFile.exists() == false) + throw new InvalidConfigurationException("FTPS trust store file does not exist, " + trustStorePath); + else if ( trustStoreFile.isDirectory()) + throw new InvalidConfigurationException("FTPS trust store path is a directory, " + trustStorePath); + + // Set the trust store path + + ftpConfig.setTrustStorePath( trustStorePath); + } + + // Check if the store passphrase has been specified + + if ( ftpConfigBean.getPassphrase() != null && ftpConfigBean.getPassphrase().length() > 0) { + + // Set the store passphrase + + ftpConfig.setPassphrase( ftpConfigBean.getPassphrase()); + } + + // Check if only secure sessions should be allowed to logon + + if ( ftpConfigBean.hasRequireSecureSession()) { + + // Only allow secure sessions to logon to the FTP server + + ftpConfig.setRequireSecureSession( true); + } + + // Check that all the required FTPS parameters have been set + + if ( ftpConfig.getKeyStorePath() != null || ftpConfig.getTrustStorePath() != null || ftpConfig.getPassphrase() != null) { + + // Make sure all parameters are set + + if ( ftpConfig.getKeyStorePath() == null || ftpConfig.getTrustStorePath() == null || ftpConfig.getPassphrase() == null) + throw new InvalidConfigurationException("FTPS configuration requires keyStore, trustStore and storePassphrase to be set"); + } + + // Check if SSLEngine debug output should be enabled + + if ( ftpConfigBean.hasSslEngineDebug()) { + + // Enable SSLEngine debug output + + System.setProperty("javax.net.debug", "ssl,handshake"); + } + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException(ex.getMessage()); + } + } + + /** + * Process the NFS server configuration + */ + protected void processNFSServerConfig() + { + // If the configuration section is not valid then NFS is disabled + + if (nfsConfigBean == null) + { + removeConfigSection(NFSConfigSection.SectionName); + return; + } + + // Check if the server has been disabled + + if (!nfsConfigBean.getServerEnabled()) + { + removeConfigSection(NFSConfigSection.SectionName); + return; + } + + // Create the NFS configuration section + + NFSConfigSection nfsConfig = new NFSConfigSection(this); + + try + { + // Check if the port mapper is enabled + + if (nfsConfigBean.getPortMapperEnabled()) + nfsConfig.setNFSPortMapper(true); + + // Check for the thread pool size + + Integer poolSize = nfsConfigBean.getThreadPool(); + + if (poolSize != null) + { + + // Range check the pool size value + + if (poolSize < 4) + throw new AlfrescoRuntimeException("NFS thread pool size is below minimum of 4"); + + // Set the thread pool size + + nfsConfig.setNFSThreadPoolSize(poolSize); + } + + // NFS packet pool size + + Integer pktPoolSize = nfsConfigBean.getPacketPool(); + + if (pktPoolSize != null) + { + // Range check the pool size value + + if (pktPoolSize < 10) + throw new AlfrescoRuntimeException("NFS packet pool size is below minimum of 10"); + + if (pktPoolSize < nfsConfig.getNFSThreadPoolSize() + 1) + throw new AlfrescoRuntimeException("NFS packet pool must be at least thread pool size plus one"); + + // Set the packet pool size + + nfsConfig.setNFSPacketPoolSize(pktPoolSize); + } + + // Check for a port mapper server port + + Integer portMapperPort = nfsConfigBean.getPortMapperPort(); + if (portMapperPort != null) + { + nfsConfig.setPortMapperPort(portMapperPort); + if ( nfsConfig.getPortMapperPort() == -1) { + logger.info("NFS portmapper registration disabled"); + } + else { + if (nfsConfig.getPortMapperPort() <= 0 || nfsConfig.getPortMapperPort() >= 65535) + throw new AlfrescoRuntimeException("Port mapper server port out of valid range"); + } + } + + // Check for a mount server port + + Integer mountServerPort = nfsConfigBean.getMountServerPort(); + if (mountServerPort != null) + { + nfsConfig.setMountServerPort(mountServerPort); + if (nfsConfig.getMountServerPort() < 0 || nfsConfig.getMountServerPort() >= 65535) + throw new AlfrescoRuntimeException("Mount server port out of valid range"); + } + + // Check for an NFS server port + + Integer nfsServerPort = nfsConfigBean.getNfsServerPort(); + if (nfsServerPort != null) + { + nfsConfig.setNFSServerPort(nfsServerPort); + if (nfsConfig.getNFSServerPort() < 0 || nfsConfig.getNFSServerPort() >= 65535) + throw new AlfrescoRuntimeException("NFS server port out of valid range"); + } + + // Check for an RPC registration port + + Integer rpcRegisterPort = nfsConfigBean.getRpcRegisterPort(); + if ( rpcRegisterPort != null) + { + nfsConfig.setRPCRegistrationPort( rpcRegisterPort); + if ( nfsConfig.getRPCRegistrationPort() < 0 || nfsConfig.getRPCRegistrationPort() >= 65535) + throw new AlfrescoRuntimeException("RPC registrtion port out of valid range"); + } + + // Check for NFS debug flags + + String flags = nfsConfigBean.getDebugFlags(); + int nfsDbg = 0; + + if (flags != null && flags.length() > 0) + { + + // Parse the flags + + flags = flags.toUpperCase(); + StringTokenizer token = new StringTokenizer(flags, ","); + + while (token.hasMoreTokens()) + { + + // Get the current debug flag token + + String dbg = token.nextToken().trim(); + + // Find the debug flag name + + int idx = 0; + + while (idx < m_nfsDebugStr.length && m_nfsDebugStr[idx].equalsIgnoreCase(dbg) == false) + idx++; + + if (idx >= m_nfsDebugStr.length) + throw new AlfrescoRuntimeException("Invalid NFS debug flag, " + dbg); + + // Set the debug flag + + nfsDbg += 1 << idx; + } + + // Set the NFS debug flags + + nfsConfig.setNFSDebug(nfsDbg); + } + + // Check if mount server debug output is enabled + + if (nfsConfigBean.getMountServerDebug()) + nfsConfig.setMountServerDebug(true); + + // Check if portmapper debug output is enabled + + if (nfsConfigBean.getPortMapperDebug()) + nfsConfig.setPortMapperDebug(true); + + // Create the RPC authenticator + RpcAuthenticator rpcAuthenticator = nfsConfigBean.getRpcAuthenticator(); + if (rpcAuthenticator != null) + { + nfsConfig.setRpcAuthenticator(rpcAuthenticator); + } + else + throw new AlfrescoRuntimeException("RPC authenticator configuration missing, require user mappings"); + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException(ex.getMessage()); + } + } + + /** + * Process the filesystems configuration + */ + protected void processFilesystemsConfig() + { + // Create the filesystems configuration section + + FilesystemsConfigSection fsysConfig = new FilesystemsConfigSection(this); + + // Access the security configuration section + + SecurityConfigSection secConfig = (SecurityConfigSection) getConfigSection(SecurityConfigSection.SectionName); + + // Process the filesystems list + + if (this.filesystemContexts != null) + { + + // Add the filesystems + + for (DeviceContext filesystem : this.filesystemContexts) + { + + // Get the current filesystem configuration + + try + { + // Check the filesystem type and use the appropriate driver + + DiskSharedDevice filesys = null; + + if (filesystem instanceof AVMContext) + { + // Create a new filesystem driver instance and register a context for + // the new filesystem + + ExtendedDiskInterface filesysDriver = getAvmDiskInterface(); + DiskDeviceContext diskCtx = (DiskDeviceContext) filesystem; + + if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) + { + if(logger.isDebugEnabled()) + { + logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); + } + GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); + HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); + hazel.initializeCache(hazelConfig, this); + diskCtx.setStateCache(hazel); + } + else + { + // Check if the filesystem uses the file state cache, if so then add to the file state reaper + StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); + standaloneCache.initializeCache( new GenericConfigElement( ""), this); + diskCtx.setStateCache(standaloneCache); + } + if ( diskCtx.hasStateCache()) { + + // Register the state cache with the reaper thread + + fsysConfig.addFileStateCache( filesystem.getDeviceName(), diskCtx.getStateCache()); + } + + filesysDriver.registerContext(filesystem); + + // Create the shared filesystem + + filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, (AVMContext)filesystem); + filesys.setConfiguration( this); + // Start the filesystem + + ((AVMContext)filesystem).startFilesystem(filesys); + } + else + { + // Create a new filesystem driver instance and register a context for + // the new filesystem + + ExtendedDiskInterface filesysDriver = getRepoDiskInterface(); + ContentContext filesysContext = (ContentContext) filesystem; + + if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) + { + if(logger.isDebugEnabled()) + { + logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ filesysContext.getShareName()); + } + GenericConfigElement hazelConfig = createClusterConfig(filesysContext.getShareName()); + HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); + hazel.initializeCache(hazelConfig, this); + filesysContext.setStateCache(hazel); + } + else + { + // Create state cache here and inject + StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); + standaloneCache.initializeCache( new GenericConfigElement( ""), this); + filesysContext.setStateCache(standaloneCache); + } + + if ( filesysContext.hasStateCache()) { + + // Register the state cache with the reaper thread + + fsysConfig.addFileStateCache( filesystem.getDeviceName(), filesysContext.getStateCache()); + + // Create the lock manager for the context. + FileStateLockManager lockMgr = new FileStateLockManager(filesysContext.getStateCache()); + filesysContext.setLockManager(lockMgr); + filesysContext.setOpLockManager(lockMgr); + } + + filesysDriver.registerContext(filesystem); + + // Check if an access control list has been specified + + AccessControlList acls = null; + AccessControlListBean accessControls = filesysContext.getAccessControlList(); + if (accessControls != null) + { + // Parse the access control list + acls = accessControls.toAccessControlList(secConfig); + } + else if (secConfig.hasGlobalAccessControls()) + { + + // Use the global access control list for this disk share + acls = secConfig.getGlobalAccessControls(); + } + + // Create the shared filesystem + + filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, filesysContext); + filesys.setConfiguration( this); + + // Add any access controls to the share + + filesys.setAccessControlList(acls); + + + + // Check if change notifications should be enabled + + if ( filesysContext.getDisableChangeNotifications() == false) + filesysContext.enableChangeHandler( true); + + // Start the filesystem + + filesysContext.startFilesystem(filesys); + } + + // Add the new filesystem + + fsysConfig.addShare(filesys); + } + catch (DeviceContextException ex) + { + throw new AlfrescoRuntimeException("Error creating filesystem " + filesystem.getDeviceName(), ex); + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException(ex.getMessage(), ex); + } + } + } + else + { + // No filesystems defined + + logger.warn("No filesystems defined"); + } + + // Check if shares should be added for all AVM stores + if (this.avmAllStores && 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 (fsysConfig.getShares().findShare(storeName, ShareType.DISK, true) == null) + { + // Create the new share for the store + + AVMContext avmContext = new AVMContext(storeName, storeName + ":/", AVMContext.VERSION_HEAD); +// avmContext.enableStateCache(this, true); + + // Create the shared filesystem + + DiskSharedDevice filesys = new DiskSharedDevice(storeName, avmDriver, avmContext); + filesys.setConfiguration( this); + + fsysConfig.addShare( filesys); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("Added AVM share " + storeName); + } + } + } + } + + + // home folder share mapper could be declared in security config + } + + /** + * Process the security configuration + */ + protected void processSecurityConfig() + { + // Create the security configuration section + + SecurityConfigSection secConfig = new SecurityConfigSection(this); + + try + { + // Check if global access controls have been specified + + AccessControlListBean accessControls = securityConfigBean.getGlobalAccessControl(); + + if (accessControls != null) + { + // Parse the access control list + AccessControlList acls = accessControls.toAccessControlList(secConfig); + if (acls != null) + secConfig.setGlobalAccessControls(acls); + } + + + // Check if a JCE provider class has been specified + + String jceProvider = securityConfigBean.getJCEProvider(); + if (jceProvider != null && jceProvider.length() > 0) + { + + // Set the JCE provider + + secConfig.setJCEProvider(jceProvider); + } + else + { + // Use the default Bouncy Castle JCE provider + + secConfig.setJCEProvider("org.bouncycastle.jce.provider.BouncyCastleProvider"); + } + + // Check if a share mapper has been specified + + ShareMapper shareMapper = securityConfigBean.getShareMapper(); + if (shareMapper != null) + { + // Associate the share mapper + secConfig.setShareMapper(shareMapper); + } + else + { + // Check if the tenant service is enabled + if (m_tenantService != null && m_tenantService.isEnabled()) + { + // Initialize the multi-tenancy share mapper + + secConfig.setShareMapper("org.alfresco.filesys.alfresco.MultiTenantShareMapper", + new GenericConfigElement("shareMapper")); + + } + } + + // Check if any domain mappings have been specified + + List mappings = securityConfigBean.getDomainMappings(); + if (mappings != null) + { + DomainMapping mapping = null; + + for (DomainMappingConfigBean domainMap : mappings) + { + // Get the domain name + + String name = domainMap.getName(); + + // Check if the domain is specified by subnet or range + + String subnetStr = domainMap.getSubnet(); + String rangeFromStr; + if (subnetStr != null && subnetStr.length() > 0) + { + String maskStr = domainMap.getMask(); + + // Parse the subnet and mask, to validate and convert to int values + + int subnet = IPAddress.parseNumericAddress(subnetStr); + int mask = IPAddress.parseNumericAddress(maskStr); + + if (subnet == 0 || mask == 0) + throw new AlfrescoRuntimeException("Invalid subnet/mask for domain mapping " + name); + + // Create the subnet domain mapping + + mapping = new SubnetDomainMapping(name, subnet, mask); + } + else if ((rangeFromStr = domainMap.getRangeFrom()) != null && rangeFromStr.length() > 0) + { + String rangeToStr = domainMap.getRangeTo(); + + // Parse the range from/to values and convert to int values + + int rangeFrom = IPAddress.parseNumericAddress(rangeFromStr); + int rangeTo = IPAddress.parseNumericAddress(rangeToStr); + + if (rangeFrom == 0 || rangeTo == 0) + throw new AlfrescoRuntimeException("Invalid address range domain mapping " + name); + + // Create the subnet domain mapping + + mapping = new RangeDomainMapping(name, rangeFrom, rangeTo); + } + else + throw new AlfrescoRuntimeException("Invalid domain mapping specified"); + + // Add the domain mapping + + secConfig.addDomainMapping(mapping); + } + } + } + catch (InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException(ex.getMessage()); + } + } + + /** + * Process the core server configuration + * + * @exception InvalidConfigurationException + */ + protected void processCoreServerConfig() throws InvalidConfigurationException + { + // Create the core server configuration section + + CoreServerConfigSection coreConfig = new CoreServerConfigSection(this); + + // Check if the CIFS server is not enabled, do not create the thread/memory pools + + if ( cifsConfigBean == null || cifsConfigBean.getServerEnabled() == false) + return; + + // Check if the server core element has been specified + + if (coreServerConfigBean == null) + { + + // Configure a default memory pool + + coreConfig.setMemoryPool(DefaultMemoryPoolBufSizes, DefaultMemoryPoolInitAlloc, DefaultMemoryPoolMaxAlloc); + + // Configure a default thread pool size + + coreConfig.setThreadPool(DefaultThreadPoolInit, DefaultThreadPoolMax); + + threadPool = coreConfig.getThreadPool(); + + return; + } + + // Check if the thread pool size has been specified + + Integer initSize = coreServerConfigBean.getThreadPoolInit(); + if (initSize == null) + { + initSize = DefaultThreadPoolInit; + } + Integer maxSize = coreServerConfigBean.getThreadPoolMax(); + if (maxSize == null) + { + maxSize = DefaultThreadPoolMax; + } + + // Range check the thread pool size + + if (initSize < ThreadRequestPool.MinimumWorkerThreads) + throw new InvalidConfigurationException("Thread pool size below minimum allowed size"); + + if (initSize > ThreadRequestPool.MaximumWorkerThreads) + throw new InvalidConfigurationException("Thread pool size above maximum allowed size"); + + // Range check the maximum thread pool size + + if (maxSize < ThreadRequestPool.MinimumWorkerThreads) + throw new InvalidConfigurationException("Thread pool maximum size below minimum allowed size"); + + if (maxSize > ThreadRequestPool.MaximumWorkerThreads) + throw new InvalidConfigurationException("Thread pool maximum size above maximum allowed size"); + + if (maxSize < initSize) + throw new InvalidConfigurationException("Initial size is larger than maxmimum size"); + + // Configure the thread pool + + coreConfig.setThreadPool(initSize, maxSize); + + threadPool = coreConfig.getThreadPool(); + + // Check if thread pool debug output is enabled + + if (coreServerConfigBean.getThreadPoolDebug()) + coreConfig.getThreadPool().setDebug(true); + + // Check if the packet sizes/allocations have been specified + + List packetSizes = coreServerConfigBean.getMemoryPacketSizes(); + if (packetSizes != null) + { + + // Calculate the array size for the packet size/allocation arrays + + int elemCnt = packetSizes.size(); + + // Create the packet size, initial allocation and maximum allocation arrays + + int[] pktSizes = new int[elemCnt]; + int[] initSizes = new int[elemCnt]; + int[] maxSizes = new int[elemCnt]; + + int elemIdx = 0; + + // Process the packet size elements + for (MemoryPacketConfigBean curChild : packetSizes) + { + + // Get the packet size + + int pktSize = -1; + + Long pktSizeLong = curChild.getSize(); + if (pktSizeLong == null) + throw new InvalidConfigurationException("Memory pool packet size not specified"); + + // Parse the packet size + + try + { + pktSize = MemorySize.getByteValueInt(pktSizeLong.toString()); + } + catch (NumberFormatException ex) + { + throw new InvalidConfigurationException("Memory pool packet size, invalid size value, " + + pktSizeLong); + } + + // Make sure the packet sizes have been specified in ascending order + + if (elemIdx > 0 && pktSizes[elemIdx - 1] >= pktSize) + throw new InvalidConfigurationException( + "Invalid packet size specified, less than/equal to previous packet size"); + + // Get the initial allocation for the current packet size + Integer initAlloc = curChild.getInit(); + if (initAlloc == null) + throw new InvalidConfigurationException("Memory pool initial allocation not specified"); + + // Range check the initial allocation + + if (initAlloc < MemoryPoolMinimumAllocation) + throw new InvalidConfigurationException("Initial memory pool allocation below minimum of " + + MemoryPoolMinimumAllocation); + + if (initAlloc > MemoryPoolMaximumAllocation) + throw new InvalidConfigurationException("Initial memory pool allocation above maximum of " + + MemoryPoolMaximumAllocation); + + // Get the maximum allocation for the current packet size + + Integer maxAlloc = curChild.getMax(); + if (maxAlloc == null) + throw new InvalidConfigurationException("Memory pool maximum allocation not specified"); + + // Range check the maximum allocation + + if (maxAlloc < MemoryPoolMinimumAllocation) + throw new InvalidConfigurationException("Maximum memory pool allocation below minimum of " + + MemoryPoolMinimumAllocation); + + if (initAlloc > MemoryPoolMaximumAllocation) + throw new InvalidConfigurationException("Maximum memory pool allocation above maximum of " + + MemoryPoolMaximumAllocation); + + // Set the current packet size elements + + pktSizes[elemIdx] = pktSize; + initSizes[elemIdx] = initAlloc; + maxSizes[elemIdx] = maxAlloc; + + elemIdx++; + } + + // Check if all elements were used in the packet size/allocation arrays + + if (elemIdx < pktSizes.length) + { + + // Re-allocate the packet size/allocation arrays + + int[] newPktSizes = new int[elemIdx]; + int[] newInitSizes = new int[elemIdx]; + int[] newMaxSizes = new int[elemIdx]; + + // Copy the values to the shorter arrays + + System.arraycopy(pktSizes, 0, newPktSizes, 0, elemIdx); + System.arraycopy(initSizes, 0, newInitSizes, 0, elemIdx); + System.arraycopy(maxSizes, 0, newMaxSizes, 0, elemIdx); + + // Move the new arrays into place + + pktSizes = newPktSizes; + initSizes = newInitSizes; + maxSizes = newMaxSizes; + } + + // Configure the memory pool + + coreConfig.setMemoryPool(pktSizes, initSizes, maxSizes); + } + else + { + + // Configure a default memory pool + + coreConfig.setMemoryPool(DefaultMemoryPoolBufSizes, DefaultMemoryPoolInitAlloc, DefaultMemoryPoolMaxAlloc); + } + } + + /** + * Initialise a runtime context - not configured through spring e.g MT. + * + * TODO - what about desktop actions etc? + * + * @param diskCtx + */ + public void initialiseRuntimeContext(AlfrescoContext diskCtx) + { + if (diskCtx.getStateCache() == null) { + + // Set the state cache, use a hard coded standalone cache for now + FilesystemsConfigSection filesysConfig = (FilesystemsConfigSection) this.getConfigSection( FilesystemsConfigSection.SectionName); + + if ( filesysConfig != null) + { + + try + { + if(clusterConfigBean != null && clusterConfigBean.getClusterEnabled()) + { + if(logger.isDebugEnabled()) + { + logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); + } + GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); + HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); + hazel.initializeCache(hazelConfig, this); + diskCtx.setStateCache(hazel); + } + else + { + // Create a standalone state cache + StandaloneFileStateCache standaloneCache = new StandaloneFileStateCache(); + standaloneCache.initializeCache( new GenericConfigElement( ""), this); + filesysConfig.addFileStateCache( diskCtx.getDeviceName(), standaloneCache); + diskCtx.setStateCache( standaloneCache); + } + + FileStateLockManager lockMgr = new FileStateLockManager(diskCtx.getStateCache()); + diskCtx.setLockManager(lockMgr); + diskCtx.setOpLockManager(lockMgr); + } + catch ( InvalidConfigurationException ex) + { + throw new AlfrescoRuntimeException( "Failed to initialize standalone state cache for " + diskCtx.getDeviceName()); + } + } + } + } + + + @Override + protected void processClusterConfig() throws InvalidConfigurationException + { + +// Done by org.alfresco.jlan.server.config.ServerConfiguration.closeConfiguration +// /** +// * Close the old hazelcast configuration +// */ +// ClusterConfigSection secConfig = (ClusterConfigSection) getConfigSection(ClusterConfigSection.SectionName); +// { +// if(secConfig != null) +// { +// secConfig.closeConfig(); +// } +// } + + if (clusterConfigBean == null || !clusterConfigBean.getClusterEnabled()) + { + removeConfigSection(ClusterConfigSection.SectionName); + logger.info("Filesystem cluster cache not enabled"); + return; + } + + String clusterName = clusterConfigBean.getClusterName(); + if (clusterName == null || clusterName.length() == 0) + { + throw new InvalidConfigurationException("Cluster name not specified or invalid"); + } + + String clusterFile = clusterConfigBean.getConfigFile(); + if (clusterFile == null || clusterFile.length() == 0) + { + throw new InvalidConfigurationException("Cluster config file not specified or invalid"); + } + + // New Hazelcast instance created here within the ClusterConfigSection ClusterConfigSection jlanClusterConfig = new ClusterConfigSection(this); try @@ -2253,8 +2261,8 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean throw new InvalidConfigurationException("Unable to start filsystem cluster", e); } } - - + + private GenericConfigElement createClusterConfig(String topicName) throws InvalidConfigurationException { GenericConfigElement config = new GenericConfigElement("hazelcastStateCache"); @@ -2285,6 +2293,13 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean config.addChild(nearCacheCfg); } return config; - } - -} + } + + @Override + public void destroy() throws Exception + { + threadPool.shutdownThreadPool(); + threadPool = null; + } + +} diff --git a/source/java/org/alfresco/filesys/repo/ContentContext.java b/source/java/org/alfresco/filesys/repo/ContentContext.java index 43984a7b41..66b4f28b0c 100644 --- a/source/java/org/alfresco/filesys/repo/ContentContext.java +++ b/source/java/org/alfresco/filesys/repo/ContentContext.java @@ -32,6 +32,7 @@ import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskSharedDevice; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.FileSystem; +import org.alfresco.jlan.server.filesys.cache.FileStateLockManager; import org.alfresco.jlan.server.filesys.quota.QuotaManagerException; import org.alfresco.jlan.server.thread.ThreadRequestPool; import org.alfresco.service.cmr.repository.NodeRef; @@ -66,6 +67,10 @@ public class ContentContext extends AlfrescoContext private AccessControlListBean m_accessControlList; + // File state based lock/oplock manager + + private FileStateLockManager m_lockManager; + // Enable/disable oplocks private boolean m_oplocksDisabled; @@ -206,7 +211,15 @@ public class ContentContext extends AlfrescoContext if (m_rootPath == null || m_rootPath.length() == 0) { throw new AlfrescoRuntimeException("Device missing rootPath"); - } + } + + // Enable file state caching + + getStateCache().setCaseSensitive( false); + + // Create the file state based lock manager + + m_lockManager = new FileStateLockManager( getStateCache()); } /** @@ -279,6 +292,15 @@ public class ContentContext extends AlfrescoContext return m_oplocksDisabled; } + /** + * Return the lock manager + * + * @return FileStateLockManager + */ + public FileStateLockManager getLockManager() { + return m_lockManager; + } + /** * Determine if change notifications are disabled * @@ -385,6 +407,15 @@ public class ContentContext extends AlfrescoContext if ( coreConfig != null) m_threadPool = coreConfig.getThreadPool(); + // Start the lock manager, use the thread pool if available + + if ( getLockManager() != null) { + + // Start the lock manager + + m_lockManager.startLockManager( "OplockExpire_" + share.getName(), m_threadPool); + } + // Start the node monitor, if enabled if ( m_nodeMonitor != null) diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index 35e3980837..ccacc7e63d 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -200,8 +200,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter private BehaviourFilter policyBehaviourFilter; private NodeMonitorFactory m_nodeMonitorFactory; - private static FileStateLockManager _lockManager; - + + /** * Class constructor * @@ -774,11 +774,11 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter logger.info("Locked files will be marked as offline"); } + + // Enable file state caching -// // Enable file state caching -// // context.enableStateCache(serverConfig, true); - + // Install the node service monitor if ( !context.getDisableNodeMonitor() && m_nodeMonitorFactory != null) { @@ -789,10 +789,6 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter context.setNodeMonitor( nodeMonitor); } - // Create the lock manager - - _lockManager = new FileStateLockManager( context.getStateCache()); - // Check if oplocks are enabled if ( context.getDisableOplocks() == true) @@ -1719,7 +1715,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter // Check if the current file open allows the required shared access boolean nosharing = false; - + String noshrReason = null; + // TEST if ( params.getAccessMode() == AccessMode.NTFileGenericExecute && params.getPath().toLowerCase().endsWith( ".exe") == false) { @@ -1736,45 +1733,53 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter if ( fstate.getOpenCount() > 0) { - // Check for impersonation security level from the original process that opened the file - - if ( params.getSecurityLevel() == WinNT.SecurityImpersonation && params.getProcessId() == fstate.getProcessId()) - nosharing = false; - - // Check if the caller wants read access, check the sharing mode - - else if ( params.isReadOnlyAccess() && (fstate.getSharedAccess() & SharingMode.READ) != 0) - nosharing = false; - - // Check if the caller wants write access, check the sharing mode - - else if (( params.isReadWriteAccess() || params.isWriteOnlyAccess()) && (fstate.getSharedAccess() & SharingMode.WRITE) == 0) - { - // DEBUG - + // Check for impersonation security level from the original process that opened the file + + if ( params.getSecurityLevel() == WinNT.SecurityImpersonation && params.getProcessId() == fstate.getProcessId()) + nosharing = false; + + // Check if the caller wants read access, check the sharing mode + // Check if the caller wants write access, check if the sharing mode allows write + + else if ( params.isReadOnlyAccess() && (fstate.getSharedAccess() & SharingMode.READ) != 0) + nosharing = false; + + // Check if the caller wants write access, check the sharing mode + + else if (( params.isReadWriteAccess() || params.isWriteOnlyAccess()) && (fstate.getSharedAccess() & SharingMode.WRITE) == 0) + { + nosharing = true; + noshrReason = "Sharing mode disallows write"; + + // DEBUG + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) - logger.debug("Sharing mode disallows write access path=" + params.getPath()); - - // Access not allowed - - throw new AccessDeniedException( "Sharing mode (write)"); - } - - // Check if the file has been opened for exclusive access - - else if ( fstate.getSharedAccess() == SharingMode.NOSHARING) - nosharing = true; - - // Check if the required sharing mode is allowed by the current file open - - else if ( ( fstate.getSharedAccess() & params.getSharedAccess()) != params.getSharedAccess()) - nosharing = true; - - // Check if the caller wants exclusive access to the file - - else if ( params.getSharedAccess() == SharingMode.NOSHARING) - nosharing = true; - + logger.debug("Sharing mode disallows write access path=" + params.getPath()); + } + + // Check if the file has been opened for exclusive access + + else if ( fstate.getSharedAccess() == SharingMode.NOSHARING) { + nosharing = true; + noshrReason = "Sharing mode exclusive"; + } + + // Check if the required sharing mode is allowed by the current file open + + else if ((fstate.getSharedAccess() & params.getSharedAccess()) != params.getSharedAccess()) { + nosharing = true; + noshrReason = "Sharing mode mismatch"; + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Local share mode=0x" + Integer.toHexString(fstate.getSharedAccess()) + ", params share mode=0x" + Integer.toHexString(params.getSharedAccess())); + } + + // Check if the caller wants exclusive access to the file + + else if ( params.getSharedAccess() == SharingMode.NOSHARING) { + nosharing = true; + noshrReason = "Requestor wants exclusive mode"; + } } // Check if the file allows shared access @@ -1786,7 +1791,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter // DEBUG if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) - logger.debug("Sharing violation path=" + params.getPath() + ", sharing=0x" + Integer.toHexString(fstate.getSharedAccess())); + logger.debug("Sharing violation path=" + params.getPath() + ", sharing=0x" + Integer.toHexString(fstate.getSharedAccess()) + ",reason=" + noshrReason); // File is locked by another user @@ -4024,7 +4029,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter * @return LockManager */ public LockManager getLockManager(SrvSession sess, TreeConnection tree) { - return _lockManager; + ContentContext ctx = (ContentContext) tree.getContext(); + return ctx.getLockManager(); } /** @@ -4035,7 +4041,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter * @return OpLockManager */ public OpLockManager getOpLockManager(SrvSession sess, TreeConnection tree) { - return _lockManager; + ContentContext ctx = (ContentContext) tree.getContext(); + return ctx.getLockManager(); } /** diff --git a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java index 012bc3ca10..5dcb67bc67 100644 --- a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java +++ b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java @@ -21,6 +21,7 @@ package org.alfresco.filesys.repo; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.filesys.DiskDeviceContext; @@ -72,8 +73,8 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // Track live usage of users that are writing files - private HashMap m_liveUsage; - private Object m_addDetailsLock = new Object(); + private Map m_liveUsage; + private Object m_liveUsageLock = new Object(); // User details inactivity checker thread @@ -141,10 +142,12 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // Check if there is a live usage record for the user UserQuotaDetails userQuota = getQuotaDetails(sess, true); - if ( userQuota != null) - { - return userQuota.getAvailableSpace(); - } + if ( userQuota != null) { + synchronized (userQuota) { + return userQuota.getAvailableSpace(); + } + } + // No quota details available return 0L; @@ -177,10 +180,10 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // Check if the user has a usage quota - if ( userQuota.hasUserQuota()) { + synchronized ( userQuota) { - synchronized ( userQuota) { - + if ( userQuota.hasUserQuota()) { + // Check if the user has enough free space allocation if ( alloc > 0 && userQuota.getAvailableSpace() >= alloc) { @@ -188,12 +191,9 @@ public class ContentQuotaManager implements QuotaManager, Runnable { allowedAlloc = alloc; } } - } - else { + else { - // Update the live usage - - synchronized ( userQuota) { + // Update the live usage userQuota.addToCurrentUsage( alloc); allowedAlloc = alloc; } @@ -308,12 +308,14 @@ public class ContentQuotaManager implements QuotaManager, Runnable { } // Clear out the live usage details - - m_liveUsage.clear(); + synchronized (m_liveUsageLock) + { + m_liveUsage.clear(); + m_shutdown = true; + } // Shutdown the checker thread - m_shutdown = true; m_thread.interrupt(); } @@ -326,37 +328,40 @@ public class ContentQuotaManager implements QuotaManager, Runnable { */ private UserQuotaDetails getQuotaDetails(SrvSession sess, boolean loadDetails) { - UserQuotaDetails userQuota = null; + synchronized (m_liveUsageLock) { + + UserQuotaDetails userQuota = null; String userName = AuthenticationUtil.getFullyAuthenticatedUser(); - + if ( sess != null && userName != null) { - // Get the live usage values - + // Get the live usage values userQuota = m_liveUsage.get(userName); if ( userQuota == null && loadDetails == true) { - // User is not in the live tracking table, load details for the user + // User is not in the live tracking table, load details for the user + try { logger.debug("user is not in cache - load details"); userQuota = loadUsageDetails(userName); - } + } catch ( QuotaManagerException ex) { - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled()) { logger.debug("Unable to load usage details", ex); } - } - } - } - - // Return the user quota details - - return userQuota; + } + } + } + + // Return the user quota details + + return userQuota; + } } /** @@ -396,8 +401,6 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // Add the details to the live tracking table - synchronized ( m_addDetailsLock) { - // Check if another thread has added the details UserQuotaDetails details = m_liveUsage.get( userName); @@ -409,7 +412,6 @@ public class ContentQuotaManager implements QuotaManager, Runnable { { m_liveUsage.put( userName, quotaDetails); } - } // DEBUG @@ -479,61 +481,66 @@ public class ContentQuotaManager implements QuotaManager, Runnable { } // Check if there are any user quota details to check - - if ( m_liveUsage != null && m_liveUsage.size() > 0) + synchronized (m_liveUsageLock) { - try + if ( m_liveUsage != null && m_liveUsage.size() > 0) { - // Timestamp to check if the quota details is inactive + try + { + // Timestamp to check if the quota details is inactive - long checkTime = System.currentTimeMillis() - UserQuotaExpireInterval; + long checkTime = System.currentTimeMillis() - UserQuotaExpireInterval; - // Loop through the user quota details + // Loop through the user quota details - removeNameList.remoteAllStrings(); - Iterator userNames = m_liveUsage.keySet().iterator(); + removeNameList.remoteAllStrings(); + Iterator userNames = m_liveUsage.keySet().iterator(); - while ( userNames.hasNext()) { + while ( userNames.hasNext()) { - // Get the user quota details and check if it has been inactive in the last check interval + // Get the user quota details and check if it has been inactive in the last check interval - String userName = userNames.next(); - UserQuotaDetails quotaDetails = m_liveUsage.get( userName); + String userName = userNames.next(); + UserQuotaDetails quotaDetails = m_liveUsage.get( userName); - if ( quotaDetails.getLastUpdated() < checkTime) { - - // Add the user name to the remove list, inactive - - removeNameList.addString( userName); - } - } + synchronized (quotaDetails) { + if ( quotaDetails.getLastUpdated() < checkTime) { + + // Add the user name to the remove list, inactive + + removeNameList.addString( userName); + } + } + } - // Remove inactive records from the live quota tracking + // Remove inactive records from the live quota tracking while ( removeNameList.numberOfStrings() > 0) { - // Get the current user name and remove the record + // Get the current user name and remove the record - String userName = removeNameList.removeStringAt( 0); - UserQuotaDetails quotaDetails = m_liveUsage.remove( userName); + String userName = removeNameList.removeStringAt( 0); + UserQuotaDetails quotaDetails = m_liveUsage.remove( userName); - // DEBUG + // DEBUG - if ( logger.isDebugEnabled()) - logger.debug("Removed inactive usage tracking, " + quotaDetails); + if ( logger.isDebugEnabled()) + logger.debug("Removed inactive usage tracking, " + quotaDetails); + } + } + catch (Exception ex) + { + // Log errors if not shutting down + + if ( m_shutdown == false) + logger.debug(ex); } } - catch (Exception ex) - { - // Log errors if not shutting down - - if ( m_shutdown == false) - logger.debug(ex); - } } - } - } + } + + } public void setContentService(ContentService contentService) { diff --git a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java index c61923da3a..b0f1ffda89 100644 --- a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java +++ b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.StackTraceUtil; import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.repo.domain.schema.SchemaBootstrap; @@ -216,8 +217,8 @@ public class AuditMethodInterceptor implements MethodInterceptor } catch (Throwable e) { - // Failure to audit should not break the invocation - logger.error( + // Failure to audit must stop the process: ALF-11139 + throw new AlfrescoRuntimeException( "Failed to audit pre-invocation: \n" + " Invocation: " + mi, e); @@ -243,8 +244,8 @@ public class AuditMethodInterceptor implements MethodInterceptor } catch (Throwable e) { - // Failure to audit should not break the invocation - logger.error( + // Failure to audit must stop the process: ALF-11139 + throw new AlfrescoRuntimeException( "Failed to audit post-invocation: \n" + " Invocation: " + mi, e); diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index 98110b8e25..d5abca02f9 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -367,7 +367,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService throw new CheckOutCheckInServiceException(MSG_ALREADY_CHECKEDOUT); } - // Make sure we are no checking out a working copy node + // Make sure we are not checking out a working copy node if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) { throw new CheckOutCheckInServiceException(MSG_ERR_ALREADY_WORKING_COPY); @@ -400,10 +400,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Invoke before check out policy invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); - - // Rename the working copy - String copyName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - copyName = createWorkingCopyName(copyName); // Get the user final String userName = getUserName(); @@ -411,7 +407,12 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService NodeRef workingCopy = null; ruleService.disableRuleType(RuleType.UPDATE); try - { + { + // Rename the working copy + String copyName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String workingCopyLabel = getWorkingCopyLabel(); + copyName = createWorkingCopyName(copyName, workingCopyLabel); + // Make the working copy final QName copyQName = QName.createQName(destinationAssocQName.getNamespaceURI(), QName.createValidLocalName(copyName)); @@ -455,6 +456,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Apply the working copy aspect to the working copy Map workingCopyProperties = new HashMap(1); workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, userName); + workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_LABEL, workingCopyLabel); nodeService.addAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties); nodeService.addAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT, null); nodeService.createAssociation(nodeRef, workingCopy, ContentModel.ASSOC_WORKING_COPY_LINK); @@ -572,36 +574,43 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { String origName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String name = (String)nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); - if (hasWorkingCopyNameChanged(name, origName)) + String wcLabel = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_WORKING_COPY_LABEL); + if (wcLabel == null) + { + wcLabel = getWorkingCopyLabel(); + } + + if (hasWorkingCopyNameChanged(name, origName, wcLabel)) { // ensure working copy has working copy label in its name to avoid name clash - if (!name.contains(" " + getWorkingCopyLabel())) + if (!name.contains(" " + wcLabel)) { try { - fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); + fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name, wcLabel)); } catch (FileExistsException e) { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, wcLabel); } catch (FileNotFoundException e) { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, wcLabel); } } + String newName = getNameFromWorkingCopyName(name, wcLabel); try { // rename original to changed working name - fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); + fileFolderService.rename(nodeRef, newName); } catch (FileExistsException e) { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, newName); } catch (FileNotFoundException e) { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, newName); } } } @@ -745,14 +754,25 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } /** - * Create working copy name + * Create a working copy name using the given fileName and workingCopyLabel. The label will be inserted before + * the file extension (if present), or else appended to the name (in either case a space is prepended to the + * workingCopyLabel). + *

+ * Examples, where workingCopyLabel is "wc": + *

+ * "Myfile.txt" becomes "Myfile wc.txt", "Myfile" becomes "Myfile wc". + *

+ * In the event that fileName is empty or null, the workingCopyLabel is used for the new working copy name + *

+ * Example: "" becomes "wc". * - * @param name name - * @return working copy name + * @param name + * @param workingCopyLabel + * @return */ - public static String createWorkingCopyName(String name) + public static String createWorkingCopyName(String name, final String workingCopyLabel) { - if (getWorkingCopyLabel() != null && getWorkingCopyLabel().length() != 0) + if (workingCopyLabel != null && workingCopyLabel.length() != 0) { if (name != null && name.length() != 0) { @@ -760,30 +780,31 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService if (index > 0) { // Insert the working copy label before the file extension - name = name.substring(0, index) + " " + getWorkingCopyLabel() + name.substring(index); + name = name.substring(0, index) + " " + workingCopyLabel + name.substring(index); } else { // Simply append the working copy label onto the end of the existing name - name = name + " " + getWorkingCopyLabel(); + name = name + " " + workingCopyLabel; } } else { - name = getWorkingCopyLabel(); + name = workingCopyLabel; } } return name; } /** - * Get original name from working copy name + * Get original name from the working copy name and the cm:workingCopyLabel + * that was used to create it. * + * @param workingCopyLabel * @return original name */ - private String getNameFromWorkingCopyName(String workingCopyName) + private String getNameFromWorkingCopyName(String workingCopyName, String workingCopyLabel) { - String workingCopyLabel = getWorkingCopyLabel(); String workingCopyLabelRegEx = workingCopyLabel.replaceAll("\\(", "\\\\("); workingCopyLabelRegEx = workingCopyLabelRegEx.replaceAll("\\)", "\\\\)"); if (workingCopyName.contains(" " + workingCopyLabel)) @@ -800,13 +821,15 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Has the working copy name changed compared to the original name * - * @param name working copy name + * @param workingCopyName working copy name * @param origName original name + * @param wcLabel that was inserted into origName to create the original working copy name * @return true => if changed */ - private boolean hasWorkingCopyNameChanged(String workingCopyName, String origName) + private boolean hasWorkingCopyNameChanged(String workingCopyName, String origName, String wcLabel) { - return !workingCopyName.equals(createWorkingCopyName(origName)); + String origWorkingCopyName = createWorkingCopyName(origName, wcLabel); + return !workingCopyName.equals(origWorkingCopyName); } /** diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index b1301fd7de..5823d1fa63 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import org.alfresco.model.ContentModel; @@ -53,6 +54,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.TestWithUserUtils; +import org.springframework.extensions.surf.util.I18NUtil; /** * Version operations service implementation unit tests @@ -218,12 +220,20 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Check that the working copy owner has been set correctly assertEquals(this.userNodeRef, nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); + // Check that the working copy name has been set correctly - String name = (String)nodeService.getProperty(this.nodeRef, PROP_NAME_QNAME); - String workingCopyLabel = CheckOutCheckInServiceImpl.createWorkingCopyName(name); - String workingCopyName = (String)nodeService.getProperty(workingCopy, PROP_NAME_QNAME); - assertEquals(workingCopyLabel, workingCopyName); + String name = (String)this.nodeService.getProperty(this.nodeRef, PROP_NAME_QNAME); + String expectedWorkingCopyLabel = I18NUtil.getMessage("coci_service.working_copy_label"); + String expectedWorkingCopyName = + ((CheckOutCheckInServiceImpl)this.cociService).createWorkingCopyName(name, expectedWorkingCopyLabel); + String workingCopyName = (String)this.nodeService.getProperty(workingCopy, PROP_NAME_QNAME); + assertEquals(expectedWorkingCopyName, workingCopyName); + // Check a record has been kept of the working copy label used to create the working copy name + assertEquals( + "No record of working copy label kept", + expectedWorkingCopyLabel, + nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_LABEL)); // Ensure that the content has been copied correctly ContentReader contentReader = this.contentService.getReader(this.nodeRef, ContentModel.PROP_CONTENT); @@ -298,10 +308,63 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest NodeRef workingCopy2 = checkout(); Map versionProperties2 = new HashMap(); versionProperties2.put(Version.PROP_DESCRIPTION, "Another version test"); - cociService.checkin(workingCopy2, versionProperties2, null, true); - cociService.checkin(workingCopy2, new HashMap(), null, true); + this.cociService.checkin(workingCopy2, versionProperties2, null, true); + this.cociService.checkin(workingCopy2, new HashMap(), null, true); } + public void testCheckOutCheckInWithDifferentLocales() + { + // Check-out nodeRef using the locale fr_FR + Locale.setDefault(Locale.FRANCE); + NodeRef workingCopy = this.cociService.checkout( + this.nodeRef, + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("workingCopy")); + assertNotNull(workingCopy); + + // Check that the working copy name has been set correctly + String workingCopyName = (String) nodeService.getProperty(workingCopy, PROP_NAME_QNAME); + assertEquals("Working copy name not correct", "myDocument (Copie de Travail).doc", workingCopyName); + + // Check-in using the locale en_GB + Locale.setDefault(Locale.UK); + Map versionProperties = new HashMap(); + versionProperties.put(Version.PROP_DESCRIPTION, "This is a test version"); + cociService.checkin(workingCopy, versionProperties); + + String name = (String) nodeService.getProperty(nodeRef, PROP_NAME_QNAME); + assertEquals("Working copy label was not removed.", "myDocument.doc", name); + } + + public void testCheckOutCheckInWithAlteredWorkingCopyName() + { + // Check-out nodeRef using the locale fr_FR + Locale.setDefault(Locale.FRANCE); + NodeRef workingCopy = this.cociService.checkout( + this.nodeRef, + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("workingCopy")); + assertNotNull(workingCopy); + + // Check that the working copy name has been set correctly + String workingCopyName = (String) nodeService.getProperty(workingCopy, PROP_NAME_QNAME); + assertEquals("Working copy name not correct", "myDocument (Copie de Travail).doc", workingCopyName); + + // Alter the working copy name + nodeService.setProperty(workingCopy, PROP_NAME_QNAME, "newName (Copie de Travail).doc"); + + // Check-in using the locale en_GB + Locale.setDefault(Locale.UK); + Map versionProperties = new HashMap(); + versionProperties.put(Version.PROP_DESCRIPTION, "This is a test version"); + cociService.checkin(workingCopy, versionProperties); + + String name = (String) nodeService.getProperty(nodeRef, PROP_NAME_QNAME); + assertEquals("File not renamed correctly.", "newName.doc", name); + } + public void testCheckInWithNameChange() { // Check out the file diff --git a/source/java/org/alfresco/repo/imap/ImapMessageTest.java b/source/java/org/alfresco/repo/imap/ImapMessageTest.java index a2541b2d3c..813cb809b4 100644 --- a/source/java/org/alfresco/repo/imap/ImapMessageTest.java +++ b/source/java/org/alfresco/repo/imap/ImapMessageTest.java @@ -205,7 +205,7 @@ public class ImapMessageTest extends TestCase // Starting IMAP - imapServiceImpl.startup(); + imapServiceImpl.startupInTxn(); nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, namespaceService, false); diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java index 2cf79f3e6a..982ebe38f4 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java @@ -204,27 +204,24 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol @Override protected void onBootstrap(ApplicationEvent event) { - service.serviceRegistry.getTransactionService().getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - if (service.getImapServerEnabled()) - { - service.startup(); - } - return null; - } - }); + service.startupInTxn(); } @Override protected void onShutdown(ApplicationEvent event) { - if (service.getImapServerEnabled()) + AuthenticationUtil.runAs(new RunAsWork() { - service.shutdown(); - } + @Override + public Void doWork() throws Exception + { + if (service.getImapServerEnabled()) + { + service.shutdown(); + } + return null; + } + }, AuthenticationUtil.getSystemUserName()); } } @@ -390,6 +387,9 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol } } + /** + * This method is run as System within a single transaction on startup. + */ public void startup() { bindBehaviour(); @@ -398,61 +398,62 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol final SearchService searchService = serviceRegistry.getSearchService(); // Get NodeRefs for folders to ignore - this.ignoreExtractionFolders = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + this.ignoreExtractionFolders = new HashSet(ignoreExtractionFoldersBeans.length * 2); + + for (RepositoryFolderConfigBean ignoreExtractionFoldersBean : ignoreExtractionFoldersBeans) { - public Set doWork() throws Exception + NodeRef nodeRef = ignoreExtractionFoldersBean.getFolderPath(namespaceService, nodeService, searchService, + fileFolderService); + + if (!ignoreExtractionFolders.add(nodeRef)) { - Set result = new HashSet(ignoreExtractionFoldersBeans.length * 2); - - for (RepositoryFolderConfigBean ignoreExtractionFoldersBean : ignoreExtractionFoldersBeans) - { - NodeRef nodeRef = ignoreExtractionFoldersBean.getFolderPath( - namespaceService, nodeService, searchService, fileFolderService); - - if (!result.add(nodeRef)) - { - // It was already in the set - throw new AlfrescoRuntimeException( - "The folder extraction path has been referenced already: \n" + - " Folder: " + ignoreExtractionFoldersBean); - } - } - - return result; + // It was already in the set + throw new AlfrescoRuntimeException("The folder extraction path has been referenced already: \n" + + " Folder: " + ignoreExtractionFoldersBean); } - }, AuthenticationUtil.getSystemUserName()); + } // Locate or create IMAP home - imapHomeNodeRef = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public NodeRef doWork() throws Exception - { - return imapHomeConfigBean.getOrCreateFolderPath(namespaceService, nodeService, searchService, fileFolderService); - } - }, AuthenticationUtil.getSystemUserName()); + imapHomeNodeRef = imapHomeConfigBean.getOrCreateFolderPath(namespaceService, nodeService, searchService, fileFolderService); // Hit the mount points and warm the caches for early failure - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + for (String mountPointName : imapConfigMountPoints.keySet()) { - public Void doWork() throws Exception + for (AlfrescoImapFolder mailbox : listMailboxes(new AlfrescoImapUser(null, AuthenticationUtil + .getSystemUserName(), null), mountPointName + "*", false)) { - for (String mountPointName: imapConfigMountPoints.keySet()) - { - for (AlfrescoImapFolder mailbox : listMailboxes(new AlfrescoImapUser(null, AuthenticationUtil - .getSystemUserName(), null), mountPointName + "*", false)) - { - mailbox.getUidNext(); - } - } - return null; + mailbox.getUidNext(); } - }, AuthenticationUtil.getSystemUserName()); - + } } public void shutdown() { } + + protected void startupInTxn() + { + if (getImapServerEnabled()) + { + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + return serviceRegistry.getTransactionService().getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + startup(); + return null; + } + }); + } + }, AuthenticationUtil.getSystemUserName()); + } + } protected void bindBehaviour() { diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java index 5bcf42cbbb..fada432d22 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java @@ -10,7 +10,6 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; @@ -99,15 +98,7 @@ public class ImapServiceImplCacheTest extends TestCase imapServiceImpl.setImapHome(imapHome); // Starting IMAP - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - imapServiceImpl.startup(); - return null; - } - }); + imapServiceImpl.startupInTxn(); nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME, diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java index e0c4369141..1921d511ae 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java @@ -180,7 +180,7 @@ public class ImapServiceImplTest extends TestCase imapServiceImpl.setImapHome(imapHome); // Starting IMAP - imapServiceImpl.startup(); + imapServiceImpl.startupInTxn(); nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME, diff --git a/source/java/org/alfresco/repo/imap/LoadTester.java b/source/java/org/alfresco/repo/imap/LoadTester.java index eda81042d5..9aec62b9fd 100644 --- a/source/java/org/alfresco/repo/imap/LoadTester.java +++ b/source/java/org/alfresco/repo/imap/LoadTester.java @@ -163,7 +163,7 @@ public class LoadTester extends TestCase imapServiceImpl.setImapHome(imapHome); // Starting IMAP - imapServiceImpl.startup(); + imapServiceImpl.startupInTxn(); nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_ROOT_FOLDER_NAME, diff --git a/source/java/org/alfresco/repo/jscript/ScriptUtils.java b/source/java/org/alfresco/repo/jscript/ScriptUtils.java index 569beeef34..ad771d9374 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptUtils.java +++ b/source/java/org/alfresco/repo/jscript/ScriptUtils.java @@ -282,9 +282,10 @@ public final class ScriptUtils extends BaseScopableProcessorExtension /** * Sets current Locale from string */ - public void setLocale(String language) + public void setLocale(String localeStr) { - I18NUtil.setLocale(new Locale(language)); + Locale newLocale = I18NUtil.parseLocale(localeStr); + I18NUtil.setLocale(newLocale); } /** diff --git a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java index 710f2d583b..5fb70ae720 100644 --- a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java @@ -417,6 +417,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); configurer.setProperties(properties); configurer.setIgnoreUnresolvablePlaceholders(true); + configurer.setSearchSystemEnvironment(false); addBeanFactoryPostProcessor(configurer); // Add all the post processors of the parent, e.g. to make sure system placeholders get expanded properly diff --git a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java index 9d63b9e96c..950b9f262a 100644 --- a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -56,6 +57,8 @@ public class FullNodeServiceTest extends BaseNodeServiceTest protected void onSetUpInTransaction() throws Exception { super.onSetUpInTransaction(); + Locale.setDefault(Locale.ENGLISH); + MLPropertyInterceptor.setMLAware(false); } public void testMLTextValues() throws Exception @@ -73,7 +76,7 @@ public class FullNodeServiceTest extends BaseNodeServiceTest BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, mlTextProperty); - // Check filterered property retrieval + // Check filtered property retrieval Serializable textValueFiltered = nodeService.getProperty( rootNodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE); @@ -90,6 +93,139 @@ public class FullNodeServiceTest extends BaseNodeServiceTest propertiesFiltered.get(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE)); } + /** + * ALF-3756 - original fix didn't cope with existing MLText properties having one or more variants + * of a particular language. Upgrading to the fix would therefore not solve the problem properly. + *

+ * For example, if a property has en_GB text in it, then 'updating' that property + * with a locale of en_US will result in the addition of the en_US text rather than a true update (they're both + * English, and using two slightly differently configured browsers in this way leads to confusion). + */ + public void testMLTextUpdatedForCorrectLanguage() throws Exception + { + Locale.setDefault(Locale.UK); + MLPropertyInterceptor.setMLAware(true); + MLText mlTextProperty = new MLText(); + mlTextProperty.addValue(Locale.UK, "en_GB String"); + mlTextProperty.addValue(Locale.FRANCE, "fr_FR String"); + + // Store the MLText property + nodeService.setProperty( + rootNodeRef, + BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, + mlTextProperty); + + // Pre-test check that an MLText property has been created with the correct locale/text pairs. + Serializable textValue = nodeService.getProperty( + rootNodeRef, + BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE); + assertEquals(2, ((MLText) textValue).size()); + assertEquals("en_GB String", ((MLText) textValue).getValue(Locale.UK)); + assertEquals("fr_FR String", ((MLText) textValue).getValue(Locale.FRANCE)); + + // Enable MLText filtering - as this is how the repo will be used. + MLPropertyInterceptor.setMLAware(false); + + // Retrieve the MLText - but it is filtered into an appropriate String + textValue = nodeService.getProperty( + rootNodeRef, + BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE); + assertEquals("en_GB String", (String) textValue); + + // Update the property, only this time using a different English variant + Locale.setDefault(Locale.US); // en_US + nodeService.setProperty( + rootNodeRef, + BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, + "Not using MLText for this part."); + + // Check that the text was updated rather than added to + MLPropertyInterceptor.setMLAware(true); // no filtering - see real MLText + // Check that there are not too many English strings, we don't want one for en_GB and one for en_US + textValue = nodeService.getProperty( + rootNodeRef, + BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE); + assertEquals(2, ((MLText) textValue).size()); + assertEquals("Text wasn't updated correctly", + "Not using MLText for this part.", + ((MLText) textValue).getValue(Locale.ENGLISH)); + assertEquals("Failed to get text using locale it was added with", + "Not using MLText for this part.", + ((MLText) textValue).getClosestValue(Locale.US)); + assertEquals("Failed to get text using original locale", + "Not using MLText for this part.", + ((MLText) textValue).getClosestValue(Locale.UK)); + assertEquals("fr_FR String", ((MLText) textValue).getValue(Locale.FRANCE)); + } + + @SuppressWarnings("unchecked") + public void testMLTextCollectionUpdatedForCorrectLanguage() + { + Locale.setDefault(Locale.UK); + MLPropertyInterceptor.setMLAware(true); + + ArrayList values = new ArrayList(); + values.add(new MLText(Locale.UK, "en_GB text")); + values.add(new MLText(Locale.US, "en_US text")); + values.add(new MLText(Locale.FRANCE, "fr_FR text")); + + // Set the property with no MLText filtering + nodeService.setProperty(rootNodeRef, PROP_QNAME_MULTI_ML_VALUE, values); + + // Pre-test check + List checkValues = (List) nodeService.getProperty( + rootNodeRef, PROP_QNAME_MULTI_ML_VALUE); + assertEquals("Expected 3 MLText values back", 3, checkValues.size()); + assertEquals("en_GB text", ((MLText) checkValues.get(0)).getValue(Locale.UK)); + assertEquals("en_US text", ((MLText) checkValues.get(1)).getValue(Locale.US)); + assertEquals("fr_FR text", ((MLText) checkValues.get(2)).getValue(Locale.FRANCE)); + + // Enable MLText filtering - as this is how the repo will be used. + MLPropertyInterceptor.setMLAware(false); + + // Filtering will result in a list containing en_GB only + checkValues = (List) nodeService.getProperty( + rootNodeRef, + PROP_QNAME_MULTI_ML_VALUE); + assertEquals("Expected 1 MLText values back", 1, checkValues.size()); + assertEquals("en_GB text", (String) checkValues.get(0)); + + // Update the property, only this time using a different English variant + Locale.setDefault(Locale.US); // en_US + + values.clear(); + values.add("text 1 added using en_US"); + values.add("text 2 added using en_US"); + values.add("text 3 added using en_US"); + values.add("text 4 added using en_US"); + nodeService.setProperty(rootNodeRef, PROP_QNAME_MULTI_ML_VALUE, values); + + // Check that the text was updated correctly + MLPropertyInterceptor.setMLAware(true); // no filtering - see real MLText + checkValues = (List) nodeService.getProperty( + rootNodeRef, + PROP_QNAME_MULTI_ML_VALUE); + + assertEquals("Expected 3 MLText values back", 4, checkValues.size()); + + MLText mlText = ((MLText) checkValues.get(0)); + assertEquals("en_GB should be replaced with new, not added to", 1, mlText.size()); + assertEquals("text 1 added using en_US", mlText.getValue(Locale.ENGLISH)); + + mlText = ((MLText) checkValues.get(1)); + assertEquals("en_US should be replaced with new, not added to", 1, mlText.size()); + assertEquals("text 2 added using en_US", mlText.getValue(Locale.ENGLISH)); + + mlText = ((MLText) checkValues.get(2)); + assertEquals("en_US should be added to fr_FR", 2, mlText.size()); + assertEquals("fr_FR text", mlText.getValue(Locale.FRANCE)); + assertEquals("text 3 added using en_US", mlText.getValue(Locale.ENGLISH)); + + mlText = ((MLText) checkValues.get(3)); + assertEquals("entirely new text value should be added", 1, mlText.size()); + assertEquals("text 4 added using en_US", mlText.getValue(Locale.ENGLISH)); + } + public void testLongMLTextValues() throws Exception { StringBuilder sb = new StringBuilder(); diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index f0c6928942..4795bd4e1e 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -551,7 +552,7 @@ public class MLPropertyInterceptor implements MethodInterceptor { newMLValue = new MLText(); } - newMLValue.addValue(contentLocale, current); + replaceTextForLanguage(contentLocale, current, newMLValue); if(count < returnMLList.size()) { returnMLList.set(count, newMLValue); @@ -602,8 +603,8 @@ public class MLPropertyInterceptor implements MethodInterceptor } // Force the inbound value to be a String (it isn't MLText) String inboundValueStr = DefaultTypeConverter.INSTANCE.convert(String.class, inboundValue); - // Add it to the current MLValue - returnMLValue.put(contentLocale, inboundValueStr); + // Update the text for the appropriate language. + replaceTextForLanguage(contentLocale, inboundValueStr, returnMLValue); // Done ret = returnMLValue; } @@ -641,4 +642,32 @@ public class MLPropertyInterceptor implements MethodInterceptor } return ret; } + + /** + * Replace any text in mlText having the same language (but any variant) as contentLocale + * with updatedText keyed by the language of contentLocale. This ensures that the mlText + * will have no more than one entry for the particular language. + * + * @param contentLocale + * @param updatedText + * @param mlText + */ + private void replaceTextForLanguage(Locale contentLocale, String updatedText, MLText mlText) + { + String language = contentLocale.getLanguage(); + // Remove all text entries having the same language as the chosen contentLocale + // (e.g. if contentLocale is en_GB, then remove text for en, en_GB, en_US etc. + Iterator locales = mlText.getLocales().iterator(); + while (locales.hasNext()) + { + Locale locale = locales.next(); + if (locale.getLanguage().equals(language)) + { + locales.remove(); + } + } + + // Add the new value for the specific language + mlText.addValue(new Locale(language), updatedText); + } } diff --git a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index 22a7832bb9..b6bd092622 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -1767,6 +1767,50 @@ public class RuleServiceCoverageTest extends TestCase }); } + public void testAssociationUpdateRule() + { + //ALF-9661 test + NodeRef sourceFolder = this.nodeService.createNode( + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}sourceFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + Map params = new HashMap(1); + params.put("aspect-name", ContentModel.ASPECT_VERSIONABLE); + //create a rule that adds an aspect after a property is updated + Rule rule = createRule( + RuleType.UPDATE, + AddFeaturesActionExecuter.NAME, + params, + NoConditionEvaluator.NAME, + null); + + this.ruleService.saveRule(sourceFolder, rule); + //create folders + NodeRef testNodeOneRef = this.nodeService.createNode( + sourceFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(TEST_NAMESPACE, "original1"), + ContentModel.TYPE_CONTENT, + getContentProperties()).getChildRef(); + addContentToNode(testNodeOneRef); + + NodeRef testNodeTwoRef = this.nodeService.createNode( + sourceFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(TEST_NAMESPACE, "original2"), + ContentModel.TYPE_CONTENT, + getContentProperties()).getChildRef(); + addContentToNode(testNodeTwoRef); + //there is no aspect + assertFalse(this.nodeService.hasAspect(testNodeOneRef, ContentModel.ASPECT_VERSIONABLE)); + //create an association + this.nodeService.addAspect(testNodeOneRef, ContentModel.ASPECT_REFERENCING, null); + this.nodeService.createAssociation(testNodeOneRef, testNodeTwoRef, ContentModel.ASSOC_REFERENCES); + //there should be the versionable aspect added + assertTrue(this.nodeService.hasAspect(testNodeOneRef, ContentModel.ASPECT_VERSIONABLE)); + } + /** * Test: * rule type: outbound diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/SingleAssocRefPolicyRuleTrigger.java b/source/java/org/alfresco/repo/rule/ruletrigger/SingleAssocRefPolicyRuleTrigger.java index b020d412b7..4273268519 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/SingleAssocRefPolicyRuleTrigger.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/SingleAssocRefPolicyRuleTrigger.java @@ -18,18 +18,23 @@ */ package org.alfresco.repo.rule.ruletrigger; +import java.util.List; + import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.rule.RuleServiceException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; public class SingleAssocRefPolicyRuleTrigger extends RuleTriggerAbstractBase { - private static final String ERR_POLICY_NAME_NOT_SET = "Unable to register rule trigger since policy name has not been set."; - + private static Log logger = LogFactory.getLog(OnPropertyUpdateRuleTrigger.class); + private String policyNamespace = NamespaceService.ALFRESCO_URI; - private String policyName; public void setPolicyNamespace(String policyNamespace) @@ -47,10 +52,8 @@ public class SingleAssocRefPolicyRuleTrigger extends RuleTriggerAbstractBase */ public void registerRuleTrigger() { - if (policyName == null) - { - throw new RuleServiceException(ERR_POLICY_NAME_NOT_SET); - } + PropertyCheck.mandatory(this, "policyNamespace", policyNamespace); + PropertyCheck.mandatory(this, "policyName", policyName); this.policyComponent.bindAssociationBehaviour( QName.createQName(this.policyNamespace, this.policyName), @@ -58,8 +61,19 @@ public class SingleAssocRefPolicyRuleTrigger extends RuleTriggerAbstractBase new JavaBehaviour(this, "policyBehaviour")); } - public void policyBehaviour(AssociationRef assocRef) - { - triggerRules(assocRef.getSourceRef(), assocRef.getTargetRef()); - } + public void policyBehaviour(AssociationRef assocRef) + { + NodeRef nodeRef = assocRef.getSourceRef(); + List parentsAssocRefs = this.nodeService.getParentAssocs(nodeRef); + for (ChildAssociationRef parentAssocRef : parentsAssocRefs) + { + triggerRules(parentAssocRef.getParentRef(), nodeRef); + if (logger.isDebugEnabled() == true) + { + logger.debug( + "OnUpdateAssoc rule triggered (parent); " + + "nodeRef=" + parentAssocRef.getParentRef()); + } + } + } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java index f0b46e18e1..bce6076ba1 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java @@ -32,8 +32,8 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; import java.nio.channels.FileChannel.MapMode; +import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; @@ -74,16 +74,15 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.CorruptIndexException; -import org.apache.lucene.index.FilterIndexReader; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexReader.FieldOption; import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.index.LogDocMergePolicy; import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.SerialMergeScheduler; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermEnum; -import org.apache.lucene.index.IndexReader.FieldOption; -import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -254,7 +253,7 @@ public class IndexInfo implements IndexMonitor public static final String MAIN_READER = "MainReader"; - private static Timer timer = new Timer(true); + private static Timer timer = new Timer("IndexInfo Cleaner Deamon", true); /** * The logger. diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java index 5752903fbe..545735c709 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java @@ -642,7 +642,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl private final Map> groupParentAssocsToDelete = new TreeMap>(); private final Map> finalGroupChildAssocs = new TreeMap>(); private List personsProcessed = new LinkedList(); - private Set allZonePersons; + private Set allZonePersons = Collections.emptySet(); private Set deletionCandidates; private long latestTime; @@ -1090,9 +1090,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl public void processGroups(UserRegistry userRegistry, boolean allowDeletions, boolean splitTxns) { - // If we got back some groups, we have to cross reference them with the set of known authorities - if (allowDeletions || !this.groupParentAssocsToDelete.isEmpty()) - { + // If we got back some groups, we have to cross reference them with the set of known authorities + if (allowDeletions || !this.groupParentAssocsToCreate.isEmpty() + || !this.personParentAssocsToCreate.isEmpty()) + { final Set allZonePersons = newPersonSet(); final Set allZoneGroups = new TreeSet(); diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index bb778d18e3..3d7e889336 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -1461,7 +1461,6 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // Build an array of name filter tokens pre lowercased to test against person properties // We require that matching people have at least one match against one of these on // either their firstname or last name - // For groups, we require a match against the whole filter on the group name or display name String nameFilterLower = null; String[] nameFilters = new String[0]; if (nameFilter != null && nameFilter.length() != 0) diff --git a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java index 017e7b2fc7..42958dc200 100644 --- a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java +++ b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.NoSuchPersonException; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.usage.ContentQuotaException; @@ -516,7 +517,14 @@ public class ContentUsageImpl implements ContentUsageService, public long getUserUsage(String userName) { ParameterCheck.mandatoryString("userName", userName); - return getUserUsage(getPerson(userName), false); + + long currentUsage = 0; + NodeRef personNodeRef = getPerson(userName); + if (personNodeRef != null) + { + currentUsage = getUserUsage(personNodeRef, false); + } + return currentUsage; } public long getUserUsage(NodeRef personNodeRef, boolean removeDeltas) @@ -587,7 +595,15 @@ public class ContentUsageImpl implements ContentUsageService, NodeRef personNodeRef = null; try { - personNodeRef = personService.getPerson(userName); + // false to not force user home creation + personNodeRef = personService.getPerson(userName, false); + } + catch (NoSuchPersonException e) + { + // Can get this situation where the person does not exist and may not be created. + // Had to add this catch when not forcing user home folder creation. + // The boolean parameter to getPerson does two things. It should really be split into two booleans. + personNodeRef = null; } catch (RuntimeException e) { diff --git a/source/java/org/alfresco/repo/usage/UsageQuotaProtector.java b/source/java/org/alfresco/repo/usage/UsageQuotaProtector.java index 1d853150db..a3115c79b4 100644 --- a/source/java/org/alfresco/repo/usage/UsageQuotaProtector.java +++ b/source/java/org/alfresco/repo/usage/UsageQuotaProtector.java @@ -92,14 +92,14 @@ public class UsageQuotaProtector implements NodeServicePolicies.OnUpdateProperti Long sizeQuotaAfter = (Long)after.get(ContentModel.PROP_SIZE_QUOTA); // Check for change in sizeCurrent - if ((sizeCurrentBefore != sizeCurrentAfter) && (sizeCurrentBefore != null) && + if ((sizeCurrentBefore != null && !sizeCurrentBefore.equals(sizeCurrentAfter)) && (sizeCurrentBefore != null) && (! (authorityService.hasAdminAuthority() || AuthenticationUtil.isRunAsUserTheSystemUser()))) { throw new AlfrescoRuntimeException("Update failed: protected property 'sizeCurrent'"); } // Check for change in sizeQuota - if ((sizeQuotaBefore != sizeQuotaAfter) && (sizeQuotaBefore != null) && + if ((sizeQuotaBefore != null && !sizeQuotaBefore.equals(sizeQuotaAfter)) && (sizeQuotaBefore != null) && (! (authorityService.hasAdminAuthority() || AuthenticationUtil.isRunAsUserTheSystemUser()))) { throw new AlfrescoRuntimeException("Update failed: protected property 'sizeQuota'"); diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index 7427300770..bd81c29786 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -699,7 +699,8 @@ public class WorkflowServiceImpl implements WorkflowService // Expand authorities to include associated groups (and parent groups) List authorities = new ArrayList(); authorities.add(authority); - Set parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, authority, false); + Set parents = authorityService.getContainingAuthoritiesInZone(AuthorityType.GROUP, authority, + null, null, 100); authorities.addAll(parents); // Retrieve pooled tasks for authorities (from each of the registered diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java index 9bd874ad52..5913ae8b9f 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java @@ -25,6 +25,8 @@ import java.util.Collections; import java.util.Date; import org.alfresco.repo.lock.LockAcquisitionException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.namespace.NamespaceService; @@ -37,6 +39,7 @@ import org.jbpm.JbpmContext; import org.jbpm.db.JobSession; import org.jbpm.job.Job; import org.jbpm.job.executor.JobExecutorThread; +import org.jbpm.taskmgmt.exe.TaskInstance; /** @@ -170,6 +173,38 @@ public class AlfrescoJobExecutorThread extends JobExecutorThread */ @Override protected void executeJob(final Job jobIn) + { + // execute the job as System (ALF-10776) so transaction commit level + // operations have a security context. + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() throws Exception + { + executeJobImpl(jobIn); + return null; + } + }, getActorId(jobIn)); + + // clear authentication context for this thread + AuthenticationUtil.clearCurrentSecurityContext(); + } + + private String getActorId(final Job jobIn) + { + TaskInstance taskInstance = jobIn.getTaskInstance(); + + if (taskInstance != null) + { + String actorId = taskInstance.getActorId(); + if (actorId != null && actorId.length() > 0) + { + return actorId; + } + } + return AuthenticationUtil.getSystemUserName(); + } + + private void executeJobImpl(final Job jobIn) { if ((!isActive) || (alfrescoJobExecutor.getTransactionService().isReadOnly())) { diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java index f3e0691c33..72831b45f1 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java @@ -18,8 +18,6 @@ */ package org.alfresco.repo.workflow.jbpm; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.jbpm.JbpmContext; import org.jbpm.graph.exe.Token; import org.jbpm.job.Timer; @@ -66,40 +64,21 @@ public class AlfrescoTimer extends Timer { // establish authentication context final TaskInstance taskInstance = getTaskInstance(); - String username = getActorId(taskInstance); // execute timer - return AuthenticationUtil.runAs(new RunAsWork() + boolean deleteTimer = AlfrescoTimer.super.execute(jbpmContext); + + // End the task if timer does not repeat. + // Note the order is a little odd here as the task will be ended + // after the token has been signalled to move to the next node. + if (deleteTimer + && taskInstance != null + && taskInstance.isOpen()) { - public Boolean doWork() throws Exception - { - boolean deleteTimer = AlfrescoTimer.super.execute(jbpmContext); - // End the task if timer does not repeat. - // Note the order is a little odd here as the task will be ended - // after the token has been signalled to move to the next node. - if (deleteTimer - && taskInstance != null - && taskInstance.isOpen()) - { - taskInstance.setSignalling(false); - taskInstance.end(); - } - return deleteTimer; - } - }, username); - } - - private String getActorId(TaskInstance taskInstance) - { - if (taskInstance != null) - { - String actorId = taskInstance.getActorId(); - if (actorId != null && actorId.length() > 0) - { - return actorId; - } + taskInstance.setSignalling(false); + taskInstance.end(); } - return AuthenticationUtil.getSystemUserName(); + + return deleteTimer; } - } diff --git a/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java b/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java index e75d5b43da..f7aa425b16 100644 --- a/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java +++ b/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java @@ -26,6 +26,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.alfresco.error.AlfrescoRuntimeException; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -57,7 +58,7 @@ import org.springframework.beans.factory.InitializingBean; * * @author Derek Hulley */ -public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingBean +public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingBean, DisposableBean { private static final int DEFAULT_CORE_POOL_SIZE = 20; private static final int DEFAULT_MAXIMUM_POOL_SIZE = -1; // -1 is a sign that it must match the core pool size @@ -244,4 +245,10 @@ public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingB { this.poolName = poolName; } + + public void destroy() + { + this.instance.shutdown(); + } + } diff --git a/source/test-resources/jbpm-test/test-context.xml b/source/test-resources/jbpm-test/test-context.xml index 68e5910710..b2efdbd0e1 100644 --- a/source/test-resources/jbpm-test/test-context.xml +++ b/source/test-resources/jbpm-test/test-context.xml @@ -14,6 +14,9 @@ ${db.url}_test + + false +