diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 2156a1ae05..29db9aea30 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -532,6 +532,9 @@ + + ${version.store.versionComparatorClass} + diff --git a/config/alfresco/messages/rule-config.properties b/config/alfresco/messages/rule-config.properties index 018e28f7f4..f0945f1d3d 100644 --- a/config/alfresco/messages/rule-config.properties +++ b/config/alfresco/messages/rule-config.properties @@ -4,3 +4,6 @@ inbound.display-label=Items are created or enter this folder outbound.display-label=Items are deleted or leave this folder update.display-label=Items are updated inboundAndUpdate.display-label=Items are created, enter this folder or are updated + +# Rule error messages +cannot.create.rule.checkout.outbound=Check out action cannot be performed for the rule type outbound! \ No newline at end of file diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 847263f5cc..785e6d9aa6 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -462,6 +462,11 @@ version.store.migrateVersionStore.cronExpression=* * * * * ? 2099 version.store.migrateVersionStore.limitPerJobCycle=-1 version.store.migrateVersionStore.runAsScheduledJob=false +# Optional Comparator class name to sort versions. +# Set to: org.alfresco.repo.version.common.VersionLabelComparator +# if upgrading from a version that used unordered sequences in a cluster. +version.store.versionComparatorClass= + # Folders for storing people system.system_container.childname=sys:system system.people_container.childname=sys:people diff --git a/config/alfresco/subsystems/Search/solr/solr-search-context.xml b/config/alfresco/subsystems/Search/solr/solr-search-context.xml index 4a461d49d9..9eca80b918 100644 --- a/config/alfresco/subsystems/Search/solr/solr-search-context.xml +++ b/config/alfresco/subsystems/Search/solr/solr-search-context.xml @@ -59,6 +59,9 @@ ${solr.query.includeGroupsForRoleAdmin} + + ${solr.query.maximumResultsFromUnlimitedQuery} + diff --git a/config/alfresco/subsystems/Search/solr/solr-search.properties b/config/alfresco/subsystems/Search/solr/solr-search.properties index fc7e14c6c1..faa1b110e1 100644 --- a/config/alfresco/subsystems/Search/solr/solr-search.properties +++ b/config/alfresco/subsystems/Search/solr/solr-search.properties @@ -2,3 +2,4 @@ solr.host=localhost solr.port=8080 solr.port.ssl=8443 solr.query.includeGroupsForRoleAdmin=false +solr.query.maximumResultsFromUnlimitedQuery=${system.acl.maxPermissionChecks} \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java index 02ccbb1208..3c1adec79e 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java @@ -40,7 +40,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.MD4PasswordEncoder; import org.alfresco.repo.security.authentication.MD4PasswordEncoderImpl; import org.alfresco.repo.security.authentication.ntlm.NLTMAuthenticator; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -463,15 +462,47 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator implements AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client; if (alfClient.hasAuthenticationTicket()) { + boolean ticketFailed = false; + try { getAuthenticationService().validate(alfClient.getAuthenticationTicket()); } catch (AuthenticationException e) { - // Ticket no longer valid or maximum tickets exceeded - alfClient.setAuthenticationTicket(null); - getAuthenticationComponent().clearCurrentSecurityContext(); + // Indicate the existing ticket is bad + + ticketFailed = true; + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Failed to validate ticket, user=" + client.getUserName() + ", ticket=" + alfClient.getAuthenticationTicket()); + } + + // If the ticket did not validate then try and get a new ticket for the user + + if ( ticketFailed == true) { + + try { + String normalized = mapUserNameToPerson( client.getUserName(), false); + getAuthenticationComponent().setCurrentUser( normalized); + alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket()); + } + catch ( AuthenticationException ex) { + + // Cannot get a new ticket for the user + + if ( logger.isErrorEnabled()) { + logger.error("Failed to get new ticket for user=" + client.getUserName()); + logger.error( ex); + } + + // Clear the ticket/security context + + alfClient.setAuthenticationTicket(null); + getAuthenticationComponent().clearCurrentSecurityContext(); + } } } else @@ -608,10 +639,10 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator implements // DEBUG - if (logger.isDebugEnabled()) - { - logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); - } +// if (logger.isDebugEnabled()) +// { +// logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); +// } // // the repository is read-only, we settle for a read-only transaction if (txService.isReadOnly()) diff --git a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java index 5dcb67bc67..30e0d6f51a 100644 --- a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java +++ b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java @@ -137,7 +137,7 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // Check if content usage is enabled if ( m_usageService.getEnabled() == false) - return 0L; + return -1L; // Check if there is a live usage record for the user @@ -150,7 +150,7 @@ public class ContentQuotaManager implements QuotaManager, Runnable { // No quota details available - return 0L; + return -1L; } /** diff --git a/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java b/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java index 123982013c..0f3b248021 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java @@ -27,11 +27,13 @@ package org.alfresco.repo.bulkimport.impl; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.bulkimport.ContentDataFactory; import org.alfresco.repo.content.ContentStore; @@ -62,7 +64,6 @@ public class FilesystemContentDataFactory implements ContentDataFactory, Initial private static final Log logger = LogFactory.getLog(FilesystemContentDataFactory.class); private static final String PROTOCOL_DELIMITER = ContentStore.PROTOCOL_DELIMITER; - private static final String OS_FILE_SEPARATOR = System.getProperty("file.separator"); private MimetypeService mimetypeService; private String defaultEncoding; @@ -101,41 +102,36 @@ public class FilesystemContentDataFactory implements ContentDataFactory, Initial */ public ContentData createContentData(ContentStore store, File contentFile) { - if(!contentIsInStore(contentFile, store)) + try { - throw new IllegalArgumentException("Can't create content URL : file '" + contentFile.getAbsolutePath() + - "' is not located within the store's tree ! The store's root is :'" + store.getRootLocation()); + String rootLocation = new File(store.getRootLocation()).getCanonicalPath(); + String contentLocation = contentFile.getCanonicalPath(); + if (!contentLocation.startsWith(rootLocation + File.separator)) + { + throw new IllegalArgumentException("Can't create content URL : file '" + contentLocation + + "' is not located within the store's tree ! The store's root is :'" + rootLocation); + } + String relativeFilePath = contentLocation.substring(rootLocation.length() + File.separator.length()); + String mimetype = mimetypeService.guessMimetype(contentFile.getName()); + String encoding = defaultEncoding; + if (!contentFile.isDirectory()) + { + encoding = guessEncoding(contentFile, mimetype); + } + + ContentData contentData = new ContentData(storeProtocol + PROTOCOL_DELIMITER + relativeFilePath, mimetype, + contentFile.length(), encoding); + + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, contentFile.getName()); + contentProps.put(ContentModel.PROP_CONTENT, contentData); + + return contentData; } - - String relativeFilePath = contentFile.getAbsolutePath().replace(store.getRootLocation() + OS_FILE_SEPARATOR, ""); - String mimetype = mimetypeService.guessMimetype(contentFile.getName()); - String encoding = defaultEncoding; - if(!contentFile.isDirectory()) + catch (IOException e) { - encoding = guessEncoding(contentFile, mimetype); + throw new AlfrescoRuntimeException(e.getMessage(), e); } - - ContentData contentData = new ContentData(storeProtocol + PROTOCOL_DELIMITER + relativeFilePath, mimetype, contentFile.length(), encoding); - - Map contentProps = new HashMap(); - contentProps.put(ContentModel.PROP_NAME, contentFile.getName()); - contentProps.put(ContentModel.PROP_CONTENT, contentData); - - return contentData; - } - - /** - * Check if file is in the store's tree, by checking if the file path starts - * with the store's configured root location. - * - * @param store The {@link ContentStore} in which the file should be - * @param contentFile The {@link File} to check - * @return boolean : whether or not the file is in the expected file tree - */ - private boolean contentIsInStore(File contentFile,ContentStore store) - { - File contentStoreFile = new File(store.getRootLocation()); - return contentFile.getAbsolutePath().startsWith(contentStoreFile.getAbsolutePath()); } /** diff --git a/source/java/org/alfresco/repo/content/AbstractContentStore.java b/source/java/org/alfresco/repo/content/AbstractContentStore.java index 49d648171a..c2e0c10323 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentStore.java +++ b/source/java/org/alfresco/repo/content/AbstractContentStore.java @@ -288,21 +288,21 @@ public abstract class AbstractContentStore implements ContentStore } /** - * @return Returns Long.MAX_VALUE always + * @return Returns -1 always */ @Override public long getSpaceFree() { - return Long.MAX_VALUE; + return -1; } /** - * @return Returns Long.MAX_VALUE always + * @return Returns -1 always */ @Override public long getSpaceTotal() { - return Long.MAX_VALUE; + return -1; } /** diff --git a/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java b/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java index 7618653fac..4b8ce28a08 100644 --- a/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java +++ b/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java @@ -269,21 +269,21 @@ public abstract class AbstractRoutingContentStore implements ContentStore } /** - * @return Returns Long.MAX_VALUE always + * @return Returns -1 always */ @Override public long getSpaceFree() { - return Long.MAX_VALUE; + return -1L; } /** - * @return Returns Long.MAX_VALUE always + * @return Returns -1 always */ @Override public long getSpaceTotal() { - return Long.MAX_VALUE; + return -1L; } /** diff --git a/source/java/org/alfresco/repo/remoteconnector/RemoteConnectorServiceImpl.java b/source/java/org/alfresco/repo/remoteconnector/RemoteConnectorServiceImpl.java index b6adedc689..2c4cea98e0 100644 --- a/source/java/org/alfresco/repo/remoteconnector/RemoteConnectorServiceImpl.java +++ b/source/java/org/alfresco/repo/remoteconnector/RemoteConnectorServiceImpl.java @@ -21,6 +21,8 @@ package org.alfresco.repo.remoteconnector; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import java.util.StringTokenizer; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationException; @@ -30,10 +32,15 @@ import org.alfresco.service.cmr.remoteconnector.RemoteConnectorResponse; import org.alfresco.service.cmr.remoteconnector.RemoteConnectorServerException; import org.alfresco.service.cmr.remoteconnector.RemoteConnectorService; import org.alfresco.util.HttpClientHelper; +import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.ProxyHost; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.simple.JSONObject; @@ -58,7 +65,32 @@ public class RemoteConnectorServiceImpl implements RemoteConnectorService */ private static Log logger = LogFactory.getLog(RemoteConnectorServiceImpl.class); private static final long MAX_BUFFER_RESPONSE_SIZE = 10*1024*1024; + + private static ProxyHost httpProxyHost; + private static ProxyHost httpsProxyHost; + private static Credentials httpProxyCredentials; + private static Credentials httpsProxyCredentials; + private static AuthScope httpAuthScope; + private static AuthScope httpsAuthScope; + /** + * Initialise the HTTP Proxy Hosts and Params Factory + */ + static + { + // Create an HTTP Proxy Host if appropriate system property set + httpProxyHost = createProxyHost("http.proxyHost", "http.proxyPort", 80); + httpProxyCredentials = createProxyCredentials("http.proxyUser", "http.proxyPassword"); + httpAuthScope = createProxyAuthScope(httpProxyHost); + + // Create an HTTPS Proxy Host if appropriate system property set + httpsProxyHost = createProxyHost("https.proxyHost", "https.proxyPort", 443); + httpsProxyCredentials = createProxyCredentials("https.proxyUser", "https.proxyPassword"); + httpsAuthScope = createProxyAuthScope(httpsProxyHost); + + + } + public RemoteConnectorServiceImpl() {} @@ -102,14 +134,49 @@ public class RemoteConnectorServiceImpl implements RemoteConnectorService } } - // Log what we're doing - if (logger.isDebugEnabled()) - logger.debug("Performing " + request.getMethod() + " request to " + request.getURL()); - // Grab our thread local HttpClient instance // Remember - we must then clean it up! HttpClient httpClient = HttpClientHelper.getHttpClient(); + // The url should already be vetted by the RemoteConnectorRequest + URL url = new URL(request.getURL()); + + // Use the appropriate Proxy Host if required + if (httpProxyHost != null && url.getProtocol().equals("http") && requiresProxy(url.getHost())) + { + httpClient.getHostConfiguration().setProxyHost(httpProxyHost); + if (logger.isDebugEnabled()) + logger.debug(" - using HTTP proxy host for: " + url); + if (httpProxyCredentials != null) + { + httpClient.getState().setProxyCredentials(httpAuthScope, httpProxyCredentials); + if (logger.isDebugEnabled()) + logger.debug(" - using HTTP proxy credentials for proxy: " + httpProxyHost.getHostName()); + } + } + else if (httpsProxyHost != null && url.getProtocol().equals("https") && requiresProxy(url.getHost())) + { + httpClient.getHostConfiguration().setProxyHost(httpsProxyHost); + if (logger.isDebugEnabled()) + logger.debug(" - using HTTPS proxy host for: " + url); + if (httpsProxyCredentials != null) + { + httpClient.getState().setProxyCredentials(httpsAuthScope, httpsProxyCredentials); + if (logger.isDebugEnabled()) + logger.debug(" - using HTTPS proxy credentials for proxy: " + httpsProxyHost.getHostName()); + } + } + else + { + //host should not be proxied remove any configured proxies + httpClient.getHostConfiguration().setProxyHost(null); + httpClient.getState().clearProxyCredentials(); + } + + // Log what we're doing + if (logger.isDebugEnabled()) + logger.debug("Performing " + request.getMethod() + " request to " + request.getURL()); + // Perform the request, and wrap the response int status = -1; String statusText = null; @@ -305,4 +372,97 @@ public class RemoteConnectorServiceImpl implements RemoteConnectorService super.finalize(); } } + + /** + * Create proxy host for the given system host and port properties. + * If the properties are not set, no proxy will be created. + * + * @param hostProperty + * @param portProperty + * @param defaultPort + * + * @return ProxyHost if appropriate properties have been set, null otherwise + */ + private static ProxyHost createProxyHost(final String hostProperty, final String portProperty, final int defaultPort) + { + final String proxyHost = System.getProperty(hostProperty); + ProxyHost proxy = null; + if (proxyHost != null && proxyHost.length() != 0) + { + final String strProxyPort = System.getProperty(portProperty); + if (strProxyPort == null || strProxyPort.length() == 0) + { + proxy = new ProxyHost(proxyHost, defaultPort); + } + else + { + proxy = new ProxyHost(proxyHost, Integer.parseInt(strProxyPort)); + } + if (logger.isDebugEnabled()) + logger.debug("ProxyHost: " + proxy.toString()); + } + return proxy; + } + + /** + * Create the proxy credentials for the given proxy user and password properties. + * If the properties are not set, not credentials will be created. + * @param proxyUserProperty + * @param proxyPasswordProperty + * @return Credentials if appropriate properties have been set, null otherwise + */ + private static Credentials createProxyCredentials(final String proxyUserProperty, final String proxyPasswordProperty) + { + final String proxyUser = System.getProperty(proxyUserProperty); + final String proxyPassword = System.getProperty(proxyPasswordProperty); + Credentials creds = null; + if (StringUtils.isNotBlank(proxyUser)) + { + creds = new UsernamePasswordCredentials(proxyUser, proxyPassword); + } + return creds; + } + + /** + * Create suitable AuthScope for ProxyHost. + * If the ProxyHost is null, no AuthsScope will be created. + * @param proxyHost + * @return Authscope for provided ProxyHost, null otherwise. + */ + private static AuthScope createProxyAuthScope(final ProxyHost proxyHost) + { + AuthScope authScope = null; + if (proxyHost != null) + { + authScope = new AuthScope(proxyHost.getHostName(), proxyHost.getPort()); + } + return authScope; + } + + /** + * Return true unless the given target host is specified in the http.nonProxyHosts system property. + * See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html + * @param targetHost Non-null host name to test + * @return true if not specified in list, false if it is specifed and therefore should be excluded from proxy + */ + private boolean requiresProxy(final String targetHost) + { + boolean requiresProxy = true; + final String nonProxyHosts = System.getProperty("http.nonProxyHosts"); + if (nonProxyHosts != null) + { + StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|"); + while (tokenizer.hasMoreTokens()) + { + String pattern = tokenizer.nextToken(); + pattern = pattern.replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*"); + if (targetHost.matches(pattern)) + { + requiresProxy = false; + break; + } + } + } + return requiresProxy; + } } diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java index b65c795108..10706ef56a 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java @@ -19,12 +19,14 @@ package org.alfresco.repo.rendition.executer; -import java.io.IOException; +import java.util.Collection; +import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.rendition.RenditionServiceException; -import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NoTransformerException; @@ -185,4 +187,27 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend return options; } + + /* + * @seeorg.alfresco.repo.rendition.executer.AbstractRenderingEngine#getParameterDefinitions() + */ + protected Collection getParameterDefinitions() + { + Collection paramList = super.getParameterDefinitions(); + + paramList.add(new ParameterDefinitionImpl(PARAM_TIMEOUT_MS, DataTypeDefinition.LONG, false, + getParamDisplayLabel(PARAM_TIMEOUT_MS))); + paramList.add(new ParameterDefinitionImpl(PARAM_READ_LIMIT_TIME_MS, DataTypeDefinition.LONG, false, + getParamDisplayLabel(PARAM_READ_LIMIT_TIME_MS))); + paramList.add(new ParameterDefinitionImpl(PARAM_MAX_SOURCE_SIZE_K_BYTES, DataTypeDefinition.LONG, false, + getParamDisplayLabel(PARAM_MAX_SOURCE_SIZE_K_BYTES))); + paramList.add(new ParameterDefinitionImpl(PARAM_READ_LIMIT_K_BYTES, DataTypeDefinition.LONG, false, + getParamDisplayLabel(PARAM_READ_LIMIT_K_BYTES))); + paramList.add(new ParameterDefinitionImpl(PARAM_MAX_PAGES, DataTypeDefinition.INT, false, + getParamDisplayLabel(PARAM_MAX_PAGES))); + paramList.add(new ParameterDefinitionImpl(PARAM_PAGE_LIMIT, DataTypeDefinition.INT, false, + getParamDisplayLabel(PARAM_PAGE_LIMIT))); + + return paramList; + } } diff --git a/source/java/org/alfresco/repo/rendition/executer/ImageRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/ImageRenderingEngine.java index 38aa437489..c96a9c96a7 100644 --- a/source/java/org/alfresco/repo/rendition/executer/ImageRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/ImageRenderingEngine.java @@ -25,7 +25,6 @@ import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.content.transform.magick.ImageCropOptions; import org.alfresco.repo.content.transform.magick.ImageResizeOptions; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; -import org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -421,19 +420,6 @@ public class ImageRenderingEngine extends AbstractTransformationRenderingEngine paramList.add(new ParameterDefinitionImpl(PARAM_COMMAND_OPTIONS, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_COMMAND_OPTIONS))); - paramList.add(new ParameterDefinitionImpl(PARAM_TIMEOUT_MS, DataTypeDefinition.LONG, false, - getParamDisplayLabel(PARAM_TIMEOUT_MS))); - paramList.add(new ParameterDefinitionImpl(PARAM_READ_LIMIT_TIME_MS, DataTypeDefinition.LONG, false, - getParamDisplayLabel(PARAM_READ_LIMIT_TIME_MS))); - paramList.add(new ParameterDefinitionImpl(PARAM_MAX_SOURCE_SIZE_K_BYTES, DataTypeDefinition.LONG, false, - getParamDisplayLabel(PARAM_MAX_SOURCE_SIZE_K_BYTES))); - paramList.add(new ParameterDefinitionImpl(PARAM_READ_LIMIT_K_BYTES, DataTypeDefinition.LONG, false, - getParamDisplayLabel(PARAM_READ_LIMIT_K_BYTES))); - paramList.add(new ParameterDefinitionImpl(PARAM_MAX_PAGES, DataTypeDefinition.INT, false, - getParamDisplayLabel(PARAM_MAX_PAGES))); - paramList.add(new ParameterDefinitionImpl(PARAM_PAGE_LIMIT, DataTypeDefinition.INT, false, - getParamDisplayLabel(PARAM_PAGE_LIMIT))); - return paramList; } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java index e70bc5d3dd..e3af51831c 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java @@ -71,11 +71,10 @@ public class SolrJSONResultSet implements ResultSet * Detached result set based on that provided * @param resultSet */ - public SolrJSONResultSet(JSONObject json, SearchParameters searchParameters, NodeService nodeService) + public SolrJSONResultSet(JSONObject json, SearchParameters searchParameters, NodeService nodeService, LimitBy limitBy, int maxResults) { // Note all properties are returned as multi-valued from the WildcardField "*" definition in the SOLR schema.xml this.nodeService = nodeService; - this.resultSetMetaData = new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, searchParameters); try { JSONObject responseHeader = json.getJSONObject("responseHeader"); @@ -139,7 +138,10 @@ public class SolrJSONResultSet implements ResultSet { } - + // We'll say we were unlimited if we got a number less than the limit + this.resultSetMetaData = new SimpleResultSetMetaData( + maxResults > 0 && numberFound < maxResults ? LimitBy.UNLIMITED : limitBy, + PermissionEvaluationMode.EAGER, searchParameters); } diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java index 40554f787b..a8e8186de7 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -98,6 +98,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware private BeanFactory beanFactory; private boolean includeGroupsForRoleAdmin = false; + + private int maximumResultsFromUnlimitedQuery = Integer.MAX_VALUE; public SolrQueryHTTPClient() { @@ -165,6 +167,15 @@ public class SolrQueryHTTPClient implements BeanFactoryAware { this.includeGroupsForRoleAdmin = includeGroupsForRoleAdmin; } + + /** + * @param maximumResultsFromUnlimitedQuery + * the maximum number of results to request from an otherwise unlimited query + */ + public void setMaximumResultsFromUnlimitedQuery(int maximumResultsFromUnlimitedQuery) + { + this.maximumResultsFromUnlimitedQuery = maximumResultsFromUnlimitedQuery; + } public ResultSet executeQuery(SearchParameters searchParameters, String language) { @@ -206,18 +217,29 @@ public class SolrQueryHTTPClient implements BeanFactoryAware url.append("?wt=").append(encoder.encode("json", "UTF-8")); url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8")); + // Emulate old limiting behaviour and metadata + final LimitBy limitBy; + int maxResults = -1; if (searchParameters.getMaxItems() >= 0) { - url.append("&rows=").append(encoder.encode("" + searchParameters.getMaxItems(), "UTF-8")); + maxResults = searchParameters.getMaxItems(); + limitBy = LimitBy.FINAL_SIZE; } - else if(searchParameters.getLimitBy() == LimitBy.FINAL_SIZE) + else if(searchParameters.getLimitBy() == LimitBy.FINAL_SIZE && searchParameters.getLimit() >= 0) { - url.append("&rows=").append(encoder.encode("" + searchParameters.getLimit(), "UTF-8")); + maxResults = searchParameters.getLimit(); + limitBy = LimitBy.FINAL_SIZE; } else { - url.append("&rows=").append(encoder.encode("" + Integer.MAX_VALUE, "UTF-8")); + maxResults = searchParameters.getMaxPermissionChecks(); + if (maxResults < 0) + { + maxResults = maximumResultsFromUnlimitedQuery; + } + limitBy = LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS; } + url.append("&rows=").append(String.valueOf(maxResults)); url.append("&df=").append(encoder.encode(searchParameters.getDefaultFieldName(), "UTF-8")); url.append("&start=").append(encoder.encode("" + searchParameters.getSkipCount(), "UTF-8")); @@ -401,7 +423,7 @@ public class SolrQueryHTTPClient implements BeanFactoryAware Reader reader = new BufferedReader(new InputStreamReader(post.getResponseBodyAsStream())); // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler JSONObject json = new JSONObject(new JSONTokener(reader)); - SolrJSONResultSet results = new SolrJSONResultSet(json, searchParameters, nodeService); + SolrJSONResultSet results = new SolrJSONResultSet(json, searchParameters, nodeService, limitBy, maxResults); if (s_logger.isDebugEnabled()) { s_logger.debug("Sent :" + url); diff --git a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java index 3979c436e9..dc8aace24c 100644 --- a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java +++ b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java @@ -182,5 +182,33 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC destroy(); } + @Override + public long getSpaceFree() + { + ContentStore x = getTenantContentStore(); + if(x != null) + { + return x.getSpaceFree(); + } + else + { + return -1; + } + } + + @Override + public long getSpaceTotal() + { + ContentStore x = getTenantContentStore(); + if(x != null) + { + return x.getSpaceTotal(); + } + else + { + return -1; + } + } + protected abstract ContentStore initContentStore(ApplicationContext ctx, String contentRoot); } diff --git a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java index 0171b9fb9c..0ebb12f58a 100644 --- a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java +++ b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java @@ -31,11 +31,14 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.dictionary.DictionaryBootstrap; import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.MutableAuthenticationDao; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicy; import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -43,6 +46,7 @@ import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.version.Version; @@ -68,7 +72,12 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest protected NodeService nodeService; protected PermissionService permissionService; protected CheckOutCheckInService checkOutCheckInService; - + protected VersionMigrator versionMigrator; + protected SearchService versionSearchService; + protected DictionaryService dictionaryService; + protected PolicyComponent policyComponent; + protected BehaviourFilter policyBehaviourFilter; + /* * Data used by tests */ @@ -158,6 +167,11 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest this.nodeService = (NodeService)applicationContext.getBean("nodeService"); this.permissionService = (PermissionService)this.applicationContext.getBean("permissionService"); this.checkOutCheckInService = (CheckOutCheckInService) applicationContext.getBean("checkOutCheckInService"); + this.versionSearchService = (SearchService)this.applicationContext.getBean("versionSearchService"); + this.versionMigrator = (VersionMigrator)this.applicationContext.getBean("versionMigrator"); + this.dictionaryService = (DictionaryService)this.applicationContext.getBean("dictionaryService"); + this.policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent"); + this.policyBehaviourFilter = (BehaviourFilter)this.applicationContext.getBean("policyBehaviourFilter"); setVersionService((VersionService)applicationContext.getBean("versionService")); diff --git a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java index d0f44c5f1a..dacc2da801 100644 --- a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java +++ b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -34,7 +34,6 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.PolicyScope; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.version.common.VersionHistoryImpl; -import org.alfresco.repo.version.common.VersionHistoryImpl.VersionComparatorAsc; import org.alfresco.repo.version.common.VersionImpl; import org.alfresco.repo.version.common.VersionUtil; import org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicy; @@ -70,8 +69,6 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe private VersionServiceImpl version1Service = new VersionServiceImpl(); private VersionMigrator versionMigrator; - private static Comparator versionComparatorAsc = new VersionComparatorAsc(); - public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; @@ -779,9 +776,13 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe VersionHistory versionHistory = null; + // List of versions with current one last and root one first. List versions = getAllVersions(versionHistoryRef); - Collections.sort(versions, versionComparatorAsc); + if (versionComparatorDesc != null) + { + Collections.sort(versions, Collections.reverseOrder(versionComparatorDesc)); + } // Build the version history object boolean isRoot = true; @@ -790,7 +791,7 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe { if (isRoot == true) { - versionHistory = new VersionHistoryImpl(version); + versionHistory = new VersionHistoryImpl(version, versionComparatorDesc); isRoot = false; } else diff --git a/source/java/org/alfresco/repo/version/VersionServiceImpl.java b/source/java/org/alfresco/repo/version/VersionServiceImpl.java index 9b40713fcf..76cc2738f4 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -22,12 +22,14 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.MLPropertyInterceptor; import org.alfresco.repo.policy.BehaviourFilter; @@ -98,6 +100,8 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl implements Ve */ protected SearchService searcher; // unused + protected Comparator versionComparatorDesc; + /** * Sets the db node service, used as the version store implementation * @@ -126,6 +130,33 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl implements Ve this.policyBehaviourFilter = policyBehaviourFilter; } + /** + * Sets an optional comparator to sort a versions in descending order (eg. 2.1, 2.0, 1.1, 1.0). + * Really only needed in a 4.1.3 system (or above) that has been upgraded from an earlier system + * that used NON ordered sequence numbers in a cluster. Not something we really support but was + * the subject of MNT-226. + * @param versionComparatorClass the name of a comparator. For example + * "org.alfresco.repo.version.common.VersionLabelComparator". + */ + @SuppressWarnings("unchecked") + public void setVersionComparatorClass(String versionComparatorClass) + { + if (versionComparatorClass != null && versionComparatorClass.trim().length() != 0) + { + try + { + versionComparatorDesc = (Comparator) getClass().getClassLoader(). + loadClass(versionComparatorClass.trim()).newInstance(); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException( + "Failed to create a Comparator using the class name "+ + versionComparatorClass, e); + } + } + } + /** * Register version label policy for the specified type * @@ -747,6 +778,7 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl implements Ve { VersionHistory versionHistory = null; + // List of versions with current one last and root one first. ArrayList versionHistoryNodeRefs = new ArrayList(); NodeRef currentVersion; @@ -793,7 +825,7 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl implements Ve if (isRoot == true) { - versionHistory = new VersionHistoryImpl(version); + versionHistory = new VersionHistoryImpl(version, versionComparatorDesc); isRoot = false; } else diff --git a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java index 5a8c4a4618..fb2b1bbd86 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java @@ -21,6 +21,7 @@ package org.alfresco.repo.version; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -28,6 +29,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import junit.framework.AssertionFailedError; + import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; @@ -296,6 +299,78 @@ public class VersionServiceImplTest extends BaseVersionStoreTest addToVersionHistory(versionableNode, version4); } + /** + * Same as testGetVersionHistorySameWorkspace except that the order of + * of db ids is mixed up and a comparator is need to fix it (MNT-226). + */ + public void testIdsOutOfOrder() + { + if (versionService instanceof Version2ServiceImpl) + { + setOutOfOrderIdsVersionService("org.alfresco.repo.version.common.VersionLabelComparator"); + testGetVersionHistorySameWorkspace(); + } + } + + /** + * Same as testIdsOutOfOrder but without the comparator so should fail. + */ + public void testIdsOutOfOrderFails() + { + if (versionService instanceof Version2ServiceImpl) + { + try + { + setOutOfOrderIdsVersionService(""); + testGetVersionHistorySameWorkspace(); + fail("Expected this to fail"); + } + catch (AssertionFailedError e) + { + System.out.print("A test failed as EXPECTED: "+e.getMessage()); + } + } + } + + /** + * Sets the versionService to be one that has is db ids out of order + * so would normally have versions displayed in the wrong order. + * @param versionComparatorClass name of class to correct the situation. + */ + private void setOutOfOrderIdsVersionService(String versionComparatorClass) + { + Version2ServiceImpl versionService = new Version2ServiceImpl() + { + @Override + protected List getAllVersions(NodeRef versionHistoryRef) + { + List versions = super.getAllVersions(versionHistoryRef); + if (versions.size() > 1) + { + // Make sure the order changes + List copy = new ArrayList(versions); + do + { + Collections.shuffle(versions); + } while (versions.equals(copy)); + } + return versions; + } + }; + versionService.setNodeService(nodeService); + versionService.setDbNodeService(dbNodeService); // mtAwareNodeService + versionService.setSearcher(versionSearchService); + versionService.setDictionaryService(dictionaryService); + versionService.setPolicyComponent(policyComponent); + versionService.setPolicyBehaviourFilter(policyBehaviourFilter); + versionService.setPermissionService(permissionService); + versionService.setOnlyUseDeprecatedV1(false); + versionService.setVersionMigrator(versionMigrator); + versionService.setVersionComparatorClass(versionComparatorClass); + versionService.initialise(); + setVersionService(versionService); + } + /** * Adds another version to the version history then checks that getVersionHistory is returning * the correct data. diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java index be00b81c2c..c5d8a03b5c 100644 --- a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java +++ b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,23 +20,22 @@ package org.alfresco.repo.version.common; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.Serializable; import java.io.ObjectInputStream.GetField; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; -import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionDoesNotExistException; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionServiceException; import org.alfresco.util.EqualsHelper; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Version History implementation. @@ -45,9 +44,8 @@ import org.apache.commons.logging.LogFactory; */ public class VersionHistoryImpl implements VersionHistory { - private static Log logger = LogFactory.getLog(VersionHistoryImpl.class); - private static final long serialVersionUID = 3257001051558326840L; + /* * Error message(s) */ @@ -65,28 +63,22 @@ public class VersionHistoryImpl implements VersionHistory private HashMap versionHistory = null; /* - * Label to version object map + * Label to version object map - Iterators must be in the order entries were addded. */ - private HashMap versionsByLabel = null; + private Map versionsByLabel = null; /* - * Versions ordered by creation date (descending) + * Versions ordered by creation date (descending). */ - private static Comparator versionComparatorDesc = new VersionComparatorDesc(); + private Comparator versionComparatorDesc; - /** - * Root version - */ - private Version rootVersion; - - - /** * Constructor, ensures the root version is set. * * @param rootVersion the root version, can not be null. + * @param versionComparatorDesc optional comparator of versions. */ - public VersionHistoryImpl(Version rootVersion) + public VersionHistoryImpl(Version rootVersion, Comparator versionComparatorDesc) { if (rootVersion == null) { @@ -96,9 +88,9 @@ public class VersionHistoryImpl implements VersionHistory } this.versionHistory = new HashMap(); - this.versionsByLabel = new HashMap(); + this.versionsByLabel = new LinkedHashMap(); + this.versionComparatorDesc = versionComparatorDesc; - this.rootVersion = rootVersion; addVersion(rootVersion, null); } @@ -109,7 +101,7 @@ public class VersionHistoryImpl implements VersionHistory */ public Version getRootVersion() { - return this.rootVersion; + return versionsByLabel.values().iterator().next(); } /** @@ -119,10 +111,7 @@ public class VersionHistoryImpl implements VersionHistory */ public Version getHeadVersion() { - Collection versions = versionsByLabel.values(); - List sortedVersions = new ArrayList(versions); - Collections.sort(sortedVersions, versionComparatorDesc); - return sortedVersions.get(0); + return getAllVersions().iterator().next(); } /** @@ -135,9 +124,26 @@ public class VersionHistoryImpl implements VersionHistory */ public Collection getAllVersions() { - Collection versions = versionsByLabel.values(); + return sortDescending(versionsByLabel.values()); + } + + /** + * Sorts Versions into descending create date order (most recent first). + * @param versions Must be in order addVersion was called. + * @return + */ + private Collection sortDescending(Collection versions) + { List sortedVersions = new ArrayList(versions); - Collections.sort(sortedVersions, versionComparatorDesc); + + if (versionComparatorDesc == null) + { + Collections.reverse(sortedVersions); + } + else + { + Collections.sort(sortedVersions, versionComparatorDesc); + } return sortedVersions; } @@ -158,7 +164,8 @@ public class VersionHistoryImpl implements VersionHistory } /** - * Gets the succeeding versions of a specified version. + * Gets the succeeding versions of a specified version. If there are multiple + * Versions they are sorted into descending create date order (most recent first). * * @param version the version object * @return a collection containing the succeeding version, empty is none @@ -173,26 +180,27 @@ public class VersionHistoryImpl implements VersionHistory if (this.versionHistory.containsValue(versionLabel) == true) { - for (String key : this.versionHistory.keySet()) + for (Entry entry: versionsByLabel.entrySet()) { + String key = entry.getKey(); if (EqualsHelper.nullSafeEquals(this.versionHistory.get(key), versionLabel)) { - result.add(getVersion(key)); + result.add(entry.getValue()); } } } } - - return result; + + return sortDescending(result); } /** - * Gets a version with a specified version label. The version label is guarenteed + * Gets a version with a specified version label. The version label is guaranteed * unique within the version history. * * @param versionLabel the version label * @return the version object - * @throws VersionDoesNotExistException indicates requested version does not exisit + * @throws VersionDoesNotExistException indicates requested version does not exist */ public Version getVersion(String versionLabel) { @@ -211,7 +219,8 @@ public class VersionHistoryImpl implements VersionHistory } /** - * Add a version to the version history. + * Add a version to the version history, in the order they were + * created. *

* Used internally to build the version history tree. * @@ -230,75 +239,7 @@ public class VersionHistoryImpl implements VersionHistory } } - /** - * Version Comparator - * - * Note: Descending (last modified) date order - */ - public static class VersionComparatorDesc implements Comparator, Serializable - { - private static final long serialVersionUID = 6227528170880231770L; - - public int compare(Version v1, Version v2) - { - int result = 0; - - if ((null != v1) && (null != v2)) - { - Serializable dbIdV1 = (null != v1.getVersionProperties()) ? (v1.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); - Serializable dbIdV2 = (null != v2.getVersionProperties()) ? (v2.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); - - if ((null != dbIdV1) && (null != dbIdV2)) - { - Long id1 = (dbIdV1 instanceof Integer) ? ((Integer) dbIdV1) : ((Long) dbIdV1); - Long id2 = (dbIdV2 instanceof Integer) ? ((Integer) dbIdV2) : ((Long) dbIdV2); - result = (id2).compareTo(id1); - } - else - { - logger.warn("DB Id of versioned node is missing!"); - } - } - - return result; - } - } - - /** - * Version Comparator - * - * Note: Ascending (last modified) date order - */ - public static class VersionComparatorAsc implements Comparator, Serializable - { - private static final long serialVersionUID = 6227528170880231770L; - - public int compare(Version v1, Version v2) - { - int result = 0; - - if ((null != v1) && (null != v2)) - { - Serializable dbIdV1 = (null != v1.getVersionProperties()) ? (v1.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); - Serializable dbIdV2 = (null != v2.getVersionProperties()) ? (v2.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); - - if ((null != dbIdV1) && (null != dbIdV2)) - { - Long id1 = (dbIdV1 instanceof Integer) ? ((Integer) dbIdV1) : ((Long) dbIdV1); - Long id2 = (dbIdV2 instanceof Integer) ? ((Integer) dbIdV2) : ((Long) dbIdV2); - result = (id1).compareTo(id2); - } - else - { - logger.warn("DB Id of versioned node is missing!"); - } - } - - return result; - } - } - - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "deprecation" }) private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { GetField fields = is.readFields(); @@ -306,21 +247,30 @@ public class VersionHistoryImpl implements VersionHistory { // This is a V2.2 class // The old 'rootVersion' maps to the current 'rootVersion' - this.rootVersion = (Version) fields.get("rootVersion", null);; - // The old 'versions' maps to the current 'versionsByLabel' this.versionsByLabel = (HashMap) fields.get("versions", new HashMap()); // The old 'versionHistory' maps to the current 'versionHistory' this.versionHistory = (HashMap) fields.get("versionHistory", new HashMap()); + // Need this comparator as versionsByLabel is not a LinkedHashMap in this version + this.versionComparatorDesc = new VersionLabelComparator(); } - else + else if (fields.defaulted("versionComparatorDesc")) { - // This is a V3.1.0 to ... class + // This is a V3.1.0 class // The old 'rootVersion' maps to the current 'rootVersion' - this.rootVersion = (Version) fields.get("rootVersion", null); - // The old 'versionsByLabel' maps to the current 'versionsByLabel' this.versionsByLabel = (HashMap) fields.get("versionsByLabel", new HashMap()); // The old 'versionHistory' maps to the current 'versionHistory' this.versionHistory = (HashMap) fields.get("versionHistory", new HashMap()); + // Need this comparator as versionsByLabel is not a LinkedHashMap in this version + this.versionComparatorDesc = new VersionLabelComparator(); + } + else + { + // This is a V4.1.3 (and 4.0.2 HF) class + // The old 'rootVersion' maps to the current 'rootVersion' + this.versionsByLabel = (Map) fields.get("versionsByLabel", new LinkedHashMap()); + // The old 'versionHistory' maps to the current 'versionHistory' + this.versionHistory = (HashMap) fields.get("versionHistory", new HashMap()); + this.versionComparatorDesc = (Comparator) fields.get("versionComparatorDesc", null); } } } diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java b/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java index 9079d88b92..030239fe55 100644 --- a/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java +++ b/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java @@ -24,9 +24,13 @@ import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import junit.framework.TestCase; @@ -50,6 +54,7 @@ public class VersionHistoryImplTest extends TestCase /** * Data used in the tests */ + private NodeRef nodeRef; private Version rootVersion = null; private Version childVersion1 = null; private Version childVersion2 = null; @@ -62,27 +67,22 @@ public class VersionHistoryImplTest extends TestCase super.setUp(); // Create dummy node ref - NodeRef nodeRef = new NodeRef(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "test"), "test"); + nodeRef = new NodeRef(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "test"), "test"); - HashMap versionProperties1 = new HashMap(); - versionProperties1.put(VersionModel.PROP_VERSION_LABEL, "1"); - versionProperties1.put(VersionModel.PROP_CREATED_DATE, new Date()); - versionProperties1.put("testProperty", "testValue"); - this.rootVersion = new VersionImpl(versionProperties1, nodeRef); - - HashMap versionProperties2 = new HashMap(); - versionProperties2.put(VersionModel.PROP_VERSION_LABEL, "2"); - versionProperties2.put(VersionModel.PROP_CREATED_DATE, new Date()); - versionProperties2.put("testProperty", "testValue"); - this.childVersion1 = new VersionImpl(versionProperties2, nodeRef); - - HashMap versionProperties3 = new HashMap(); - versionProperties3.put(VersionModel.PROP_VERSION_LABEL, "3"); - versionProperties3.put(VersionModel.PROP_CREATED_DATE, new Date()); - versionProperties3.put("testProperty", "testValue"); - this.childVersion2 = new VersionImpl(versionProperties3, nodeRef); + this.rootVersion = newVersion(nodeRef, "1"); + this.childVersion1 = newVersion(nodeRef, "2"); + this.childVersion2 = newVersion(nodeRef, "3"); } + private VersionImpl newVersion(NodeRef nodeRef, String label) + { + HashMap versionProperties1 = new HashMap(); + versionProperties1.put(VersionModel.PROP_VERSION_LABEL, label); + versionProperties1.put(VersionModel.PROP_CREATED_DATE, new Date()); + versionProperties1.put("testProperty", "testValue"); + return new VersionImpl(versionProperties1, nodeRef); + } + /** * Test constructor */ @@ -98,7 +98,7 @@ public class VersionHistoryImplTest extends TestCase */ private VersionHistoryImpl testContructorImpl() { - VersionHistoryImpl vh = new VersionHistoryImpl(this.rootVersion); + VersionHistoryImpl vh = new VersionHistoryImpl(this.rootVersion, null); assertNotNull(vh); return vh; @@ -112,7 +112,7 @@ public class VersionHistoryImplTest extends TestCase { try { - new VersionHistoryImpl(null); + new VersionHistoryImpl(null, null); fail(); } catch(VersionServiceException exception) @@ -146,6 +146,42 @@ public class VersionHistoryImplTest extends TestCase assertEquals(3, allVersions.size()); } + /** + * Test getAllVersions using a comparator to resort versions which are in the + * wrong order. + */ + public void testGetAllVersionsComparator() + { + String[] labels = new String[] { "1.0", "1.1", "1.2", "2.0", "2.1" }; + List versions = new ArrayList(labels.length); + for (String label: labels) + { + versions.add(newVersion(nodeRef, label)); + } + Collections.shuffle(versions); + + Iterator itr = versions.iterator(); + Version version = itr.next(); + Version predecessor; + VersionHistoryImpl vh = new VersionHistoryImpl(version, + Collections.reverseOrder(new VersionLabelComparator())); + while (itr.hasNext()) + { + predecessor = version; + version = itr.next(); + vh.addVersion(version, predecessor); + } + + Collection allVersions = vh.getAllVersions(); + assertNotNull(allVersions); + assertEquals(labels.length, allVersions.size()); + itr = allVersions.iterator(); + for (String label: labels) + { + assertEquals(label, itr.next().getVersionLabel()); + } + } + /** * Test addVersion * @@ -210,7 +246,6 @@ public class VersionHistoryImplTest extends TestCase /** * Test getSuccessors */ - @SuppressWarnings("unchecked") public void testGetSuccessors() { VersionHistoryImpl vh = testAddVersionImpl(); @@ -237,6 +272,40 @@ public class VersionHistoryImplTest extends TestCase assertTrue(versions3.isEmpty()); } + /** + * Test getSuccessors using a comparator to resort versions which are in the + * wrong order. + */ + public void testGetSuccessorsComparator() + { + rootVersion = newVersion(nodeRef, "1.0"); + String[] labels = new String[] { "1.1", "1.2", "2.0", "2.1" }; + List versions = new ArrayList(labels.length); + for (String label: labels) + { + versions.add(newVersion(nodeRef, label)); + } + Collections.shuffle(versions); + + Iterator itr = versions.iterator(); + Version version = rootVersion; + VersionHistoryImpl vh = new VersionHistoryImpl(version, + Collections.reverseOrder(new VersionLabelComparator())); + while (itr.hasNext()) + { + vh.addVersion(itr.next(), rootVersion); + } + + Collection allVersions = vh.getSuccessors(rootVersion); + assertNotNull(allVersions); + assertEquals(labels.length, allVersions.size()); + itr = allVersions.iterator(); + for (String label: labels) + { + assertEquals(label, itr.next().getVersionLabel()); + } + } + /** * Test getVersion */ diff --git a/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java b/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java index 03368956b1..7ed1b14aa2 100644 --- a/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java +++ b/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -28,7 +28,7 @@ import org.alfresco.util.VersionNumber; * * @author Yanick Pignot * - * @deprecated see VersionHistory (getAllVersions, VersionComparatorAsc, VersionComparatorDesc) + * @deprecated See {@link VersionHistory} */ public class VersionLabelComparator implements Comparator {