diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.desc.xml new file mode 100644 index 0000000000..e1e56a5808 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.desc.xml @@ -0,0 +1,5 @@ + + Local test + /cmissample/local + user + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.html.ftl b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.html.ftl new file mode 100644 index 0000000000..2d29d2b1c5 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.html.ftl @@ -0,0 +1,49 @@ + + + CMIS Connection + + + +

Repository Info

+ + + + + + + +
Id:${cmisSession.repositoryInfo.id}
Name:${cmisSession.repositoryInfo.name}
Description:${cmisSession.repositoryInfo.description}
Vendor:${cmisSession.repositoryInfo.vendorName}
Product name:${cmisSession.repositoryInfo.productName}
Product version:${cmisSession.repositoryInfo.productVersion}
+ +

Root folder

+ Root folder Id: ${cmisSession.rootFolder.id}

+ + + + + <#list rootFolderChildren as obj> + + + + + + +
Name + Id + Type +
${obj.name}${obj.id}${obj.type.id}
+ +

Base Types

+ + + + <#list baseTypes as bt> + + + + + +
Name + Id +
${bt.displayName}${bt.id}
+ + diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.js b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.js new file mode 100644 index 0000000000..9cb4aecdd2 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmislocal.get.js @@ -0,0 +1,9 @@ +// get the local or default remote connection +var cmisConnection = cmisclient.getConnection(); + +// get CMIS session +var cmisSession = cmisConnection.getSession(); + +model.cmisSession = cmisSession; +model.rootFolderChildren = cmisSession.getRootFolder().getChildren().iterator(); +model.baseTypes = cmisSession.getTypeChildren(null, false).iterator(); \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.desc.xml new file mode 100644 index 0000000000..33bf9c1f0e --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.desc.xml @@ -0,0 +1,4 @@ + + CMIS servers + /cmissample/servers + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.html.ftl b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.html.ftl new file mode 100644 index 0000000000..4ad03b3335 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.html.ftl @@ -0,0 +1,29 @@ + + + CMIS Server Defintions + + + +

Configured server definitions

+ + + + + <#list cmisServers as s> + + + + + + +
Name + Description + Parameters +
${s.name}${s.description} + <#assign p = s.parameters> + <#list p?keys as key> + ${key} = ${p[key]}
+ +
+ + diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.js b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.js new file mode 100644 index 0000000000..766350eeb0 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/client/sample/cmisserver.get.js @@ -0,0 +1,2 @@ +// get all configured CMIS servers +model.cmisServers = cmisclient.getServerDefinitions(); diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodeContent.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodeContent.get.desc.xml new file mode 100644 index 0000000000..3322d660a2 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodeContent.get.desc.xml @@ -0,0 +1,9 @@ + + Get node property content as text + Get the content for the node property as text. + /api/solr/{nodeId}/{propertyQName?}/textContent + argument + admin + required + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.desc.xml similarity index 77% rename from config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.desc.xml rename to config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.desc.xml index e13345fe5d..5290da85ef 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.desc.xml @@ -1,8 +1,9 @@ Get the nodes in the given transactions Get the nodes updated/deleted in the given transactions. - /api/solr/nodes?txnIds={txnIds}&{fromNodeId?}&{toNodeId?}&{count?} + /api/solr/nodes argument admin required + internal \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.json.ftl similarity index 86% rename from config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.json.ftl rename to config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.json.ftl index 326c334e6c..8686e0ecfc 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.get.json.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodes.post.json.ftl @@ -6,6 +6,5 @@ <@solrLib.nodeJSON node=node/> <#if node_has_next>, - ], - "count": ${count} + ] } \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.desc.xml new file mode 100644 index 0000000000..ade11bebbd --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.desc.xml @@ -0,0 +1,9 @@ + + Get the metadata for the specified nodes + Get the metadata for the specified nodes. + /api/solr/metadata + argument + admin + required + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.json.ftl new file mode 100644 index 0000000000..8c45102f11 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getNodesMetaData.post.json.ftl @@ -0,0 +1,9 @@ +<#import "solr.lib.ftl" as solrLib/> +{ + "nodes" : + [ + <#list nodes as nodeMetaData> + <@solrLib.nodeMetaDataJSON nodeMetaData=nodeMetaData filter=filter/><#if nodeMetaData_has_next>, + + ] +} \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getTransactions.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getTransactions.get.desc.xml index 571da798db..5ddf6dd1ac 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getTransactions.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/getTransactions.get.desc.xml @@ -1,8 +1,9 @@ Get transactions Get the transactions from the given commit time. - /api/solr/transactions?fromCommitTime={fromCommitTime} + /api/solr/transactions?fromTxnId={fromTxnId?}&fromCommitTime={fromCommitTime?}&maxResults={maxResults?} argument admin required + internal \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/solr.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/solr.lib.ftl index 552aa77c9a..0f5d86858f 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/solr/solr.lib.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/solr/solr.lib.ftl @@ -1,16 +1,67 @@ <#macro transactionJSON txn> -{ - "id": "${txn.id?c}", - "commitTimeMs": "${txn.commitTimeMs?c}", - "updates": "${txn.updates?c}", - "deletes": "${txn.deletes?c}" -} + { + "id": ${txn.id?c}, + "commitTimeMs": ${txn.commitTimeMs?c}, + "updates": ${txn.updates?c}, + "deletes": ${txn.deletes?c} + } <#macro nodeJSON node> -{ - "nodeID": "${node.id?c}", - "txnID": "${node.transaction.id?c}", - "deleted": "${node.deleted?string}" -} - \ No newline at end of file + { + "id": ${node.id?c}, + "txnId": ${node.transaction.id?c}, + "status": "<#if node.deleted>d<#else>u" + } + + +<#-- +TODO property values: string, number, date (at the moment all output as string) +--> +<#macro nodeMetaDataJSON nodeMetaData filter> + { + "id": ${nodeMetaData.nodeId?c}, + "nodeRef": <#if filter.includeNodeRef??>"${nodeMetaData.nodeRef.toString()}", + "type": <#if filter.includeType??><@qNameJSON qName=nodeMetaData.nodeType/>, + "aclId": <#if filter.includeAclId??><#if nodeMetaData.aclId??>${nodeMetaData.aclId?c}<#else>null, + <#if filter.includeProperties??> + "properties": { + <#list nodeMetaData.properties?keys as propQName> + <@qNameJSON qName=propQName/>: ${nodeMetaData.properties[propQName]}<#if propQName_has_next>, + + }, + + <#if filter.includeAspects??> + "aspects": [ + <#list nodeMetaData.aspects as aspectQName> + <@nodeAspectJSON aspectQName=aspectQName indent=""/><#if aspectQName_has_next>, + + ], + + <#if filter.includePaths??> + "paths": [ + <#list nodeMetaData.paths as path> + <@pathJSON path=path indent=""/><#if path_has_next>, + + ] + + } + + +<#macro pathJSON path indent=""> +${indent}"${path.toString()}" + + +<#macro qNameJSON qName indent=""> +${indent}"${jsonUtils.encodeJSONString(shortQName(qName))}" + + +<#macro nodePropertyJSON propQName propValue> +<@qNameJSON qName=propQName/>: <#if propValue??>"propValue"<#else>null + + +<#macro nodeAspectJSON aspectQName indent=""> +${indent}<@qNameJSON qName=aspectQName/> + + + diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 329290e6e3..0375c344ff 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -142,6 +142,9 @@ + + + @@ -362,6 +365,48 @@ + + + + + + + + + + + + + + + + + + + + @@ -1181,16 +1226,35 @@ + + + + - - + + + diff --git a/source/java/org/alfresco/repo/cmis/client/AbstractCMISConnectionManagerImpl.java b/source/java/org/alfresco/repo/cmis/client/AbstractCMISConnectionManagerImpl.java new file mode 100644 index 0000000000..cd3151773f --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/AbstractCMISConnectionManagerImpl.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.service.cmr.security.AuthenticationService; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; +import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.springframework.beans.factory.InitializingBean; + +public abstract class AbstractCMISConnectionManagerImpl implements CMISConnectionManager, InitializingBean +{ + public static final String SERVER_NAME = "name"; + public static final String SERVER_DESCRIPTION = "description"; + public static final String LOCAL_CONNECTION_ID = "local"; + public static final char RESERVED_ID_CHAR = '$'; + + protected AuthenticationService authenticationService; + + protected SessionFactory sessionFactory; + + protected LinkedHashMap sharedConnections; + protected LinkedHashMap userConnections; + protected int userConnectionsCapacity = 1000; + protected int sharedConnectionsCapacity = 1000; + + protected Map servers; + + protected ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setUserConnectionsCapacity(int userConnectionsCapacity) + { + this.userConnectionsCapacity = userConnectionsCapacity; + } + + public void setSharedConnectionsCapacity(int sharedConnectionsCapacity) + { + this.sharedConnectionsCapacity = sharedConnectionsCapacity; + } + + public void setServers(List> serverList) + { + servers = new HashMap(); + + for (Map serverData : serverList) + { + CMISServer server = createServerDefinition(serverData); + if (server != null) + { + servers.put(server.getName(), server); + } + } + } + + @Override + public void afterPropertiesSet() throws Exception + { + lock.writeLock().lock(); + try + { + sessionFactory = SessionFactoryImpl.newInstance(); + + sharedConnections = new LinkedHashMap(sharedConnectionsCapacity, + (int) Math.ceil(sharedConnectionsCapacity / 0.75) + 1, true) + { + private static final long serialVersionUID = 1; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > sharedConnectionsCapacity; + } + }; + + userConnections = new LinkedHashMap(userConnectionsCapacity, + (int) Math.ceil(userConnectionsCapacity / 0.75) + 1, true) + { + private static final long serialVersionUID = 1; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > userConnectionsCapacity; + } + }; + } finally + { + lock.writeLock().unlock(); + } + } + + @Override + public CMISConnection createUserConnection(CMISServer server, String connectionId) + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + String userConnectionId = createUserConnectionId(currentUser, connectionId); + + CMISConnection connection; + + lock.writeLock().lock(); + try + { + if (userConnections.containsKey(userConnectionId)) + { + throw new IllegalStateException("Connection id is already in use!"); + } + + connection = createConnection(server, userConnectionId); + + userConnections.put(userConnectionId, connection); + } finally + { + lock.writeLock().unlock(); + } + + return connection; + } + + protected String createUserConnectionId(String username, String connectionId) + { + return connectionId + RESERVED_ID_CHAR + username; + } + + @Override + public CMISConnection createSharedConnection(CMISServer server, String connectionId) + { + CMISConnection connection; + + lock.writeLock().lock(); + try + { + if (sharedConnections.containsKey(connectionId)) + { + throw new IllegalStateException("Connection id is already in use!"); + } + + connection = createConnection(server, connectionId); + + sharedConnections.put(connection.getId(), connection); + } finally + { + lock.writeLock().unlock(); + } + + return connection; + } + + protected CMISConnection createConnection(CMISServer server, String connectionId) + { + if (connectionId == null || connectionId.length() == 0 || connectionId.indexOf(RESERVED_ID_CHAR) > -1) + { + throw new IllegalArgumentException("Invalid connection id!"); + } + + Session session = createSession(server.getParameters()); + String username = server.getParameters().get(SessionParameter.USER); + + return new CMISConnectionImpl(this, connectionId, session, server, username, false); + } + + @Override + public abstract CMISConnection getConnection(); + + public Collection getUserConnections() + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + lock.writeLock().lock(); + try + { + List result = new ArrayList(); + + for (CMISConnection conn : userConnections.values()) + { + int idx = conn.getId().indexOf(RESERVED_ID_CHAR); + if (idx > -1) + { + if (currentUser.equals(conn.getId().substring(idx + 1))) + { + result.add(conn); + } + } + } + + return Collections.unmodifiableList(result); + } finally + { + lock.writeLock().unlock(); + } + } + + public CMISConnection getUserConnections(String connectionId) + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + lock.writeLock().lock(); + try + { + String userConnectionId = createUserConnectionId(currentUser, connectionId); + return userConnections.get(userConnectionId); + } finally + { + lock.writeLock().unlock(); + } + } + + public Collection getSharedConnections() + { + lock.writeLock().lock(); + try + { + return Collections.unmodifiableCollection(sharedConnections.values()); + } finally + { + lock.writeLock().unlock(); + } + } + + public CMISConnection getSharedConnection(String connectionId) + { + lock.writeLock().lock(); + try + { + return sharedConnections.get(connectionId); + } finally + { + lock.writeLock().unlock(); + } + } + + public void removeConnection(CMISConnection connection) + { + if (connection == null || connection.getId() == null) + { + return; + } + + lock.writeLock().lock(); + try + { + if (connection.isLocal()) + { + userConnections.remove(LOCAL_CONNECTION_ID); + } else + { + int idx = connection.getId().indexOf(RESERVED_ID_CHAR); + if (idx == -1) + { + sharedConnections.remove(connection.getId()); + } else + { + userConnections.remove(connection.getId()); + } + } + } finally + { + lock.writeLock().unlock(); + } + } + + protected Session createSession(Map parameters) + { + if (parameters.containsKey(SessionParameter.REPOSITORY_ID)) + { + return sessionFactory.createSession(new HashMap(parameters)); + } else + { + return sessionFactory.getRepositories(new HashMap(parameters)).get(0).createSession(); + } + } + + @Override + public Collection getServerDefinitions() + { + return servers == null ? null : servers.values(); + } + + @Override + public CMISServer getServerDefinition(String serverName) + { + return servers == null ? null : servers.get(serverName); + } + + @Override + public CMISServer createServerDefinition(String serverName, Map parameters) + { + return new CMISServerImpl(serverName, null, parameters); + } + + @Override + public CMISServer createServerDefinition(CMISServer server, String username, String password) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + Map parameters = new HashMap(server.getParameters()); + parameters.put(SessionParameter.USER, username); + parameters.put(SessionParameter.PASSWORD, password); + + return new CMISServerImpl(server.getName(), server.getDescription(), parameters); + } + + @Override + public CMISServer createServerDefinition(CMISServer server, String username, String password, String repositoryId) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + Map parameters = new HashMap(server.getParameters()); + parameters.put(SessionParameter.USER, username); + parameters.put(SessionParameter.PASSWORD, password); + parameters.put(SessionParameter.REPOSITORY_ID, repositoryId); + + return new CMISServerImpl(server.getName(), server.getDescription(), parameters); + } + + protected CMISServer createServerDefinition(Map parameters) + { + if (parameters == null) + { + throw new IllegalArgumentException("Parameters must be set!"); + } + + String name = parameters.get(SERVER_NAME); + parameters.remove(SERVER_NAME); + + String description = parameters.get(SERVER_DESCRIPTION); + parameters.remove(SERVER_DESCRIPTION); + + if (name != null) + { + return new CMISServerImpl(name, description, parameters); + } + + return null; + } + + @Override + public List getRepositories(CMISServer server) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + return sessionFactory.getRepositories(new HashMap(server.getParameters())); + } +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISConnection.java b/source/java/org/alfresco/repo/cmis/client/CMISConnection.java new file mode 100644 index 0000000000..d533c4d9fe --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISConnection.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import org.apache.chemistry.opencmis.client.api.Session; + +/** + * Represents a CMIS connection. + */ +public interface CMISConnection +{ + /** + * Gets connection id. + * + * @return connection id + */ + String getId(); + + /** + * Gets OpenCMIS Session. + * + * @return OpenCMIS session + */ + Session getSession(); + + /** + * Gets the CMIS Server. + * + * @return CMIS Server + */ + CMISServer getServer(); + + /** + * Gets the user name. + * + * @return user name + */ + String getUserName(); + + /** + * Indicates if the connection is a local or a remote connection. + */ + boolean isLocal(); + + /** + * Releases the CMIS session and removes the connection from connection + * manager. + */ + void close(); +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISConnectionCache.java b/source/java/org/alfresco/repo/cmis/client/CMISConnectionCache.java new file mode 100644 index 0000000000..54a2587504 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISConnectionCache.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +public class CMISConnectionCache +{ + private int capacity; + + public CMISConnectionCache(int capacity) + { + this.capacity = capacity; + } + + public void add(CMISConnection connection) + { + + } + + public void remove(CMISConnection connection) + { + + } + + +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISConnectionImpl.java b/source/java/org/alfresco/repo/cmis/client/CMISConnectionImpl.java new file mode 100644 index 0000000000..58ff1c564b --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISConnectionImpl.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import org.apache.chemistry.opencmis.client.api.Session; + +public class CMISConnectionImpl implements CMISConnection +{ + private AbstractCMISConnectionManagerImpl connectionManager; + + private String id; + private Session session; + private CMISServer server; + private String username; + private boolean isLocal; + + public CMISConnectionImpl(AbstractCMISConnectionManagerImpl connectionManager, String id, Session session, + CMISServer server, String username, boolean isLocal) + { + if (connectionManager == null) + { + throw new IllegalArgumentException("Connection Manager must be set!"); + } + + if (id == null) + { + throw new IllegalArgumentException("Id must be set!"); + } + + if (session == null) + { + throw new IllegalArgumentException("Session must be set!"); + } + + this.connectionManager = connectionManager; + this.id = id; + this.session = session; + this.server = server; + this.username = username; + this.isLocal = isLocal; + } + + @Override + public String getId() + { + return id; + } + + @Override + public Session getSession() + { + return session; + } + + @Override + public CMISServer getServer() + { + return server; + } + + @Override + public String getUserName() + { + return username; + } + + @Override + public boolean isLocal() + { + return isLocal; + } + + @Override + public void close() + { + connectionManager.removeConnection(this); + session = null; + } +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISConnectionManager.java b/source/java/org/alfresco/repo/cmis/client/CMISConnectionManager.java new file mode 100644 index 0000000000..907d684786 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISConnectionManager.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.chemistry.opencmis.client.api.Repository; + +/** + * Manages all CMIS client connections. + */ +public interface CMISConnectionManager +{ + // --- connections --- + + /** + * Creates a new connection that is only visible to the current user. + */ + CMISConnection createUserConnection(CMISServer server, String connectionId); + + /** + * Creates a new connection that is visible to all users. + */ + CMISConnection createSharedConnection(CMISServer server, String connectionId); + + /** + * Gets or creates a connection to the local server or a default server. + */ + CMISConnection getConnection(); + + /** + * Returns all user connections. + */ + Collection getUserConnections(); + + /** + * Returns a specific user connection or null if the connection + * id is unknown. + */ + CMISConnection getUserConnections(String connectionId); + + /** + * Returns all shared connections. + */ + Collection getSharedConnections(); + + /** + * Returns a specific shared connection or null if the + * connection id is unknown. + */ + CMISConnection getSharedConnection(String connectionId); + + // --- servers --- + + /** + * Returns all configured server definitions. + */ + Collection getServerDefinitions(); + + /** + * Gets a server definitions by name. + */ + CMISServer getServerDefinition(String serverName); + + /** + * Creates a new server definition. + */ + CMISServer createServerDefinition(String serverName, Map parameters); + + /** + * Creates a new server definition from a template. + */ + CMISServer createServerDefinition(CMISServer server, String username, String password); + + /** + * Creates a new server definition from a template. + */ + CMISServer createServerDefinition(CMISServer server, String username, String password, String repositoryId); + + // --- repositories --- + + /** + * Returns all repositories available at this server. + */ + List getRepositories(CMISServer server); +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISConnectionManagerImpl.java b/source/java/org/alfresco/repo/cmis/client/CMISConnectionManagerImpl.java new file mode 100644 index 0000000000..348ec36293 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISConnectionManagerImpl.java @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.service.cmr.security.AuthenticationService; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; +import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.enums.BindingType; +import org.springframework.beans.factory.InitializingBean; + +public class CMISConnectionManagerImpl implements CMISConnectionManager, InitializingBean +{ + public static final String SERVER_NAME = "name"; + public static final String SERVER_DESCRIPTION = "description"; + public static final String LOCAL_CONNECTION_ID = "local"; + public static final char RESERVED_ID_CHAR = '$'; + + private AuthenticationService authenticationService; + + private SessionFactory sessionFactory; + + private LinkedHashMap sharedConnections; + private LinkedHashMap userConnections; + private int userConnectionsCapacity = 1000; + private int sharedConnectionsCapacity = 1000; + + private Map servers; + + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public CMISConnectionManagerImpl() + { + } + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setServers(List> serverList) + { + servers = new HashMap(); + + for (Map serverData : serverList) + { + String name = serverData.get(SERVER_NAME); + serverData.remove(SERVER_NAME); + + String description = serverData.get(SERVER_DESCRIPTION); + serverData.remove(SERVER_DESCRIPTION); + + if (name != null) + { + CMISServer server = new CMISServerImpl(name, description, serverData); + servers.put(server.getName(), server); + } + } + } + + @Override + public void afterPropertiesSet() throws Exception + { + lock.writeLock().lock(); + try + { + sessionFactory = SessionFactoryImpl.newInstance(); + + sharedConnections = new LinkedHashMap(sharedConnectionsCapacity, + (int) Math.ceil(sharedConnectionsCapacity / 0.75) + 1, true) + { + private static final long serialVersionUID = 1; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > sharedConnectionsCapacity; + } + }; + + userConnections = new LinkedHashMap(userConnectionsCapacity, + (int) Math.ceil(userConnectionsCapacity / 0.75) + 1, true) + { + private static final long serialVersionUID = 1; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > userConnectionsCapacity; + } + }; + } finally + { + lock.writeLock().unlock(); + } + } + + @Override + public CMISConnection createUserConnection(CMISServer server, String connectionId) + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + String userConnectionId = createUserConnectionId(currentUser, connectionId); + + CMISConnection connection; + + lock.writeLock().lock(); + try + { + if (userConnections.containsKey(userConnectionId)) + { + throw new IllegalStateException("Connection id is already in use!"); + } + + connection = createConnection(server, userConnectionId); + + userConnections.put(connection.getId(), connection); + } finally + { + lock.writeLock().unlock(); + } + + return connection; + } + + private String createUserConnectionId(String username, String connectionId) + { + return connectionId + RESERVED_ID_CHAR + username; + } + + @Override + public CMISConnection createSharedConnection(CMISServer server, String connectionId) + { + CMISConnection connection; + + lock.writeLock().lock(); + try + { + if (sharedConnections.containsKey(connectionId)) + { + throw new IllegalStateException("Connection id is already in use!"); + } + + connection = createConnection(server, connectionId); + + sharedConnections.put(connection.getId(), connection); + } finally + { + lock.writeLock().unlock(); + } + + return connection; + } + + private CMISConnection createConnection(CMISServer server, String connectionId) + { + if (connectionId == null || connectionId.length() == 0 || connectionId.indexOf(RESERVED_ID_CHAR) > -1) + { + throw new IllegalArgumentException("Invalid connection id!"); + } + + Session session = sessionFactory.createSession(server.getParameters()); + String username = server.getParameters().get(SessionParameter.USER); + + return new CMISConnectionImpl(this, connectionId, session, server, username, false); + } + + @Override + public CMISConnection getLocalConnection() + { + CMISConnection connection = getUserConnections(LOCAL_CONNECTION_ID); + if (connection != null) + { + return connection; + } + + Map parameters = new HashMap(); + parameters.put(SessionParameter.BINDING_TYPE, BindingType.LOCAL.value()); + // ToDo: add factory + + CMISServer server = createServerDefinition("local", parameters); + + Session session = sessionFactory.createSession(server.getParameters()); + + connection = new CMISConnectionImpl(this, LOCAL_CONNECTION_ID, session, server, null, true); + + lock.writeLock().lock(); + try + { + userConnections.put(connection.getId(), connection); + } finally + { + lock.writeLock().unlock(); + } + + return connection; + } + + public Collection getUserConnections() + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + lock.writeLock().lock(); + try + { + List result = new ArrayList(); + + for (CMISConnection conn : userConnections.values()) + { + int idx = conn.getId().indexOf(RESERVED_ID_CHAR); + if (idx > -1) + { + if (currentUser.equals(conn.getId().substring(idx + 1))) + { + result.add(conn); + } + } + } + + return Collections.unmodifiableList(result); + } finally + { + lock.writeLock().unlock(); + } + } + + public CMISConnection getUserConnections(String connectionId) + { + String currentUser = authenticationService.getCurrentUserName(); + if (currentUser == null) + { + throw new IllegalStateException("No current user!"); + } + + lock.writeLock().lock(); + try + { + String userConnectionId = createUserConnectionId(currentUser, connectionId); + return userConnections.get(userConnectionId); + } finally + { + lock.writeLock().unlock(); + } + } + + public Collection getSharedConnections() + { + lock.writeLock().lock(); + try + { + return Collections.unmodifiableCollection(sharedConnections.values()); + } finally + { + lock.writeLock().unlock(); + } + } + + public CMISConnection getSharedConnection(String connectionId) + { + lock.writeLock().lock(); + try + { + return sharedConnections.get(connectionId); + } finally + { + lock.writeLock().unlock(); + } + } + + public void removeConnection(CMISConnection connection) + { + if (connection == null || connection.getId() == null) + { + return; + } + + lock.writeLock().lock(); + try + { + if (connection.isLocal()) + { + userConnections.remove(LOCAL_CONNECTION_ID); + } else + { + int idx = connection.getId().indexOf(RESERVED_ID_CHAR); + if (idx == -1) + { + sharedConnections.remove(connection.getId()); + } else + { + userConnections.remove(connection.getId()); + } + } + } finally + { + lock.writeLock().unlock(); + } + } + + @Override + public Collection getServerDefinitions() + { + return servers == null ? null : servers.values(); + } + + @Override + public CMISServer getServerDefinition(String serverName) + { + return servers == null ? null : servers.get(serverName); + } + + @Override + public CMISServer createServerDefinition(String serverName, Map parameters) + { + return new CMISServerImpl(serverName, null, parameters); + } + + @Override + public CMISServer createServerDefinition(CMISServer server, String username, String password) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + Map parameters = new HashMap(server.getParameters()); + parameters.put(SessionParameter.USER, username); + parameters.put(SessionParameter.PASSWORD, password); + + return new CMISServerImpl(server.getName(), server.getDescription(), parameters); + } + + @Override + public CMISServer createServerDefinition(CMISServer server, String username, String password, String repositoryId) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + Map parameters = new HashMap(server.getParameters()); + parameters.put(SessionParameter.USER, username); + parameters.put(SessionParameter.PASSWORD, password); + parameters.put(SessionParameter.REPOSITORY_ID, repositoryId); + + return new CMISServerImpl(server.getName(), server.getDescription(), parameters); + } + + @Override + public List getRepositories(CMISServer server) + { + if (server == null) + { + throw new IllegalArgumentException("Server must be set!"); + } + + return sessionFactory.getRepositories(server.getParameters()); + } +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISLocalConnectionManagerImpl.java b/source/java/org/alfresco/repo/cmis/client/CMISLocalConnectionManagerImpl.java new file mode 100644 index 0000000000..3410fc77e6 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISLocalConnectionManagerImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.opencmis.AlfrescoLocalCmisServiceFactory; +import org.alfresco.opencmis.CMISConnector; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.enums.BindingType; + +/** + * CMIS Connection manager with a local default connection. + */ +public class CMISLocalConnectionManagerImpl extends AbstractCMISConnectionManagerImpl implements CMISConnectionManager +{ + /** + * Sets the CMIS connector. + */ + public void setCmisConnector(CMISConnector connector) + { + AlfrescoLocalCmisServiceFactory.setCmisConnector(connector); + } + + /** + * Creates a local connection. + */ + @Override + public CMISConnection getConnection() + { + lock.writeLock().lock(); + try + { + CMISConnection connection = getUserConnections(LOCAL_CONNECTION_ID); + if (connection != null) + { + return connection; + } + + String currentUser = authenticationService.getCurrentUserName(); + + Map parameters = new HashMap(); + parameters.put(SessionParameter.BINDING_TYPE, BindingType.LOCAL.value()); + parameters.put(SessionParameter.LOCAL_FACTORY, AlfrescoLocalCmisServiceFactory.class.getName()); + parameters.put(SessionParameter.USER, currentUser); + + CMISServer server = createServerDefinition("local", parameters); + Session session = createSession(server.getParameters()); + connection = new CMISConnectionImpl(this, LOCAL_CONNECTION_ID, session, server, currentUser, true); + + String userConnectionId = createUserConnectionId(currentUser, LOCAL_CONNECTION_ID); + + userConnections.put(userConnectionId, connection); + + return connection; + } finally + { + lock.writeLock().unlock(); + } + } +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISRemoteConnectionManagerImpl.java b/source/java/org/alfresco/repo/cmis/client/CMISRemoteConnectionManagerImpl.java new file mode 100644 index 0000000000..2a36854ba2 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISRemoteConnectionManagerImpl.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.Map; + +/** + * CMIS Connection manager with a remote default connection. + */ +public class CMISRemoteConnectionManagerImpl extends AbstractCMISConnectionManagerImpl implements CMISConnectionManager +{ + private CMISServer defaultServer; + + /** + * Sets the remote server details. + */ + public void setDefaultServer(Map defaultServerProperties) + { + defaultServer = createServerDefinition(defaultServerProperties); + } + + /** + * Creates a remote connection. + */ + @Override + public CMISConnection getConnection() + { + lock.writeLock().lock(); + try + { + CMISConnection connection = getUserConnections(LOCAL_CONNECTION_ID); + if (connection != null) + { + return connection; + } + + String currentUser = authenticationService.getCurrentUserName(); + + if (defaultServer == null) + { + throw new IllegalStateException("No default server defined!"); + } + + CMISServer server = createServerDefinition(defaultServer, currentUser, null); + + return createUserConnection(server, LOCAL_CONNECTION_ID); + } finally + { + lock.writeLock().unlock(); + } + } +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISServer.java b/source/java/org/alfresco/repo/cmis/client/CMISServer.java new file mode 100644 index 0000000000..090a5a4c40 --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISServer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.Map; + +/** + * CMIS server definition. + */ +public interface CMISServer +{ + /** + * Gets CMIS Server Name + * + * @return server name + */ + String getName(); + + /** + * Gets CMIS Server Description + * + * @return server description + */ + String getDescription(); + + /** + * Gets CMIS Server Parameters + * + * @return server parameters + */ + Map getParameters(); +} diff --git a/source/java/org/alfresco/repo/cmis/client/CMISServerImpl.java b/source/java/org/alfresco/repo/cmis/client/CMISServerImpl.java new file mode 100644 index 0000000000..3cc91d7b6f --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/client/CMISServerImpl.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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.repo.cmis.client; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class CMISServerImpl implements CMISServer +{ + private String name; + private String description; + private Map parameters; + + public CMISServerImpl(String name, String description, Map parameters) + { + if (name == null) + { + throw new IllegalArgumentException("Name must be set!"); + } + if (parameters == null) + { + throw new IllegalArgumentException("Parameters must be set!"); + } + + this.name = name; + this.description = description; + this.parameters = new HashMap(parameters); + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getDescription() + { + return description; + } + + @Override + public Map getParameters() + { + return Collections.unmodifiableMap(parameters); + } +} diff --git a/source/java/org/alfresco/repo/cmis/ws/AuthenticationInterceptor.java b/source/java/org/alfresco/repo/cmis/ws/AuthenticationInterceptor.java index 690cd77c55..dd49757353 100644 --- a/source/java/org/alfresco/repo/cmis/ws/AuthenticationInterceptor.java +++ b/source/java/org/alfresco/repo/cmis/ws/AuthenticationInterceptor.java @@ -20,8 +20,8 @@ package org.alfresco.repo.cmis.ws; import java.util.List; +import org.alfresco.repo.security.authentication.Authorization; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.repo.web.util.auth.Authorization; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.transaction.TransactionService; import org.apache.cxf.binding.soap.SoapMessage; diff --git a/source/java/org/alfresco/repo/web/scripts/servlet/BasicHttpAuthenticatorFactory.java b/source/java/org/alfresco/repo/web/scripts/servlet/BasicHttpAuthenticatorFactory.java index f82089abb0..58533c905a 100644 --- a/source/java/org/alfresco/repo/web/scripts/servlet/BasicHttpAuthenticatorFactory.java +++ b/source/java/org/alfresco/repo/web/scripts/servlet/BasicHttpAuthenticatorFactory.java @@ -22,14 +22,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.alfresco.repo.security.authentication.AuthenticationException; -import org.alfresco.repo.web.util.auth.Authorization; +import org.alfresco.repo.security.authentication.Authorization; import org.alfresco.service.cmr.security.AuthenticationService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.Base64; import org.springframework.extensions.webscripts.Authenticator; -import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.Description.RequiredAuthentication; +import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory; import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest; import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse; diff --git a/source/java/org/alfresco/repo/web/scripts/solr/GetNodes.java b/source/java/org/alfresco/repo/web/scripts/solr/GetNodes.java index da1b403a1f..45d5a4f9d0 100644 --- a/source/java/org/alfresco/repo/web/scripts/solr/GetNodes.java +++ b/source/java/org/alfresco/repo/web/scripts/solr/GetNodes.java @@ -1,5 +1,6 @@ package org.alfresco.repo.web.scripts.solr; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -14,6 +15,10 @@ import org.alfresco.repo.domain.solr.SOLRDAO.NodeQueryCallback; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.extensions.surf.util.Content; import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptException; @@ -49,84 +54,97 @@ public class GetNodes extends DeclarativeWebScript @Override protected Map executeImpl(WebScriptRequest req, Status status) { - String txnIdsString = req.getParameter("txnIds"); - if(txnIdsString == null) + try { - throw new WebScriptException("txnIds parameter is required for GetNodes"); - } - - String param = req.getParameter("fromNodeId"); - Long fromNodeId = (param == null ? null : Long.valueOf(param)); - - param = req.getParameter("toNodeId"); - Long toNodeId = (param == null ? null : Long.valueOf(param)); - - param = req.getParameter("excludeAspects"); - Set excludeAspects = null; - if(param != null) - { - String[] excludeAspectsStrings = param.split(","); - excludeAspects = new HashSet(excludeAspectsStrings.length); - for(String excludeAspect : excludeAspectsStrings) + Content content = req.getContent(); + if(content == null) { - excludeAspects.add(QName.createQName(excludeAspect.trim())); + throw new WebScriptException("Failed to convert request to String"); } - } - - param = req.getParameter("includeAspects"); - Set includeAspects = null; - if(param != null) - { - String[] includeAspectsStrings = param.split(","); - includeAspects = new HashSet(includeAspectsStrings.length); - for(String includeAspect : includeAspectsStrings) + JSONObject o = new JSONObject(content.getContent()); + + JSONArray aTxnIds = o.has("txnIds") ? o.getJSONArray("txnIds") : null; + Long fromTxnId = o.has("fromTxnId") ? o.getLong("fromTxnId") : null; + Long toTxnId = o.has("toTxnId") ? o.getLong("toTxnId") : null; + + Long fromNodeId = o.has("fromNodeId") ? o.getLong("fromNodeId") : null; + Long toNodeId = o.has("toNodeId") ? o.getLong("toNodeId") : null; + + Set excludeAspects = null; + if(o.has("excludeAspects")) { - includeAspects.add(QName.createQName(includeAspect.trim())); + JSONArray aExcludeAspects = o.getJSONArray("excludeAspects"); + excludeAspects = new HashSet(aExcludeAspects.length()); + for(int i = 0; i < aExcludeAspects.length(); i++) + { + excludeAspects.add(QName.createQName(aExcludeAspects.getString(i).trim())); + } } + + Set includeAspects = null; + if(o.has("includeAspects")) + { + JSONArray aIncludeAspects = o.getJSONArray("includeAspects"); + includeAspects = new HashSet(aIncludeAspects.length()); + for(int i = 0; i < aIncludeAspects.length(); i++) + { + includeAspects.add(QName.createQName(aIncludeAspects.getString(i).trim())); + } + } + + // 0 or Integer.MAX_VALUE => ignore + int maxResults = o.has("maxResults") ? o.getInt("maxResults") : 0; + + String storeProtocol = o.has("storeProtocol") ? o.getString("storeProtocol") : null; + String storeIdentifier = o.has("storeIdentifier") ? o.getString("storeIdentifier") : null; + + List txnIds = null; + if(aTxnIds != null) + { + txnIds = new ArrayList(aTxnIds.length()); + for(int i = 0; i < aTxnIds.length(); i++) + { + txnIds.add(aTxnIds.getLong(i)); + } + } + + WebNodeQueryCallback nodeQueryCallback = new WebNodeQueryCallback(maxResults); + NodeParameters nodeParameters = new NodeParameters(); + nodeParameters.setTransactionIds(txnIds); + nodeParameters.setFromTxnId(fromTxnId); + nodeParameters.setToTxnId(toTxnId); + nodeParameters.setFromNodeId(fromNodeId); + nodeParameters.setToNodeId(toNodeId); + nodeParameters.setExcludeAspects(excludeAspects); + nodeParameters.setIncludeAspects(includeAspects); + nodeParameters.setStoreProtocol(storeProtocol); + nodeParameters.setStoreIdentifier(storeIdentifier); + nodeParameters.setMaxResults(maxResults); + solrDAO.getNodes(nodeParameters, nodeQueryCallback); + + Map model = new HashMap(1, 1.0f); + List nodes = nodeQueryCallback.getNodes(); + model.put("nodes", nodes); + + if (logger.isDebugEnabled()) + { + logger.debug("Result: \n\tRequest: " + req + "\n\tModel: " + model); + } + + return model; } - - param = req.getParameter("maxResults"); - int maxResults = (param == null ? 0 : Integer.valueOf(param)); - - param = req.getParameter("storeProtocol"); - String storeProtocol = param; - - param = req.getParameter("storeIdentifier"); - String storeIdentifier = param; - - String[] txnIdStrings = txnIdsString.split(","); - List txnIds = new ArrayList(txnIdStrings.length); - for(String txnIdString : txnIdStrings) + catch(IOException e) { - txnIds.add(Long.valueOf(txnIdString.trim())); + throw new WebScriptException("IO exception parsing request", e); } - - WebNodeQueryCallback nodeQueryCallback = new WebNodeQueryCallback(maxResults); - NodeParameters nodeParameters = new NodeParameters(); - nodeParameters.setTransactionIds(txnIds); - nodeParameters.setFromNodeId(fromNodeId); - nodeParameters.setToNodeId(toNodeId); - nodeParameters.setExcludeAspects(excludeAspects); - nodeParameters.setIncludeAspects(includeAspects); - nodeParameters.setStoreProtocol(storeProtocol); - nodeParameters.setStoreIdentifier(storeIdentifier); - solrDAO.getNodes(nodeParameters, maxResults, nodeQueryCallback); - - Map model = new HashMap(2, 1.0f); - List nodes = nodeQueryCallback.getNodes(); - model.put("nodes", nodes); - model.put("count", nodes.size()); - - if (logger.isDebugEnabled()) + catch(JSONException e) { - logger.debug("Result: \n\tRequest: " + req + "\n\tModel: " + model); + throw new WebScriptException("Invalid JSON", e); } - - return model; } /** - * + * Callback for DAO get nodes query * */ private static class WebNodeQueryCallback implements NodeQueryCallback diff --git a/source/java/org/alfresco/repo/web/scripts/solr/GetNodesMetaData.java b/source/java/org/alfresco/repo/web/scripts/solr/GetNodesMetaData.java new file mode 100644 index 0000000000..e342b0ae1d --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/solr/GetNodesMetaData.java @@ -0,0 +1,308 @@ +package org.alfresco.repo.web.scripts.solr; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.ContentDataWithId; +import org.alfresco.repo.domain.solr.MetaDataResultsFilter; +import org.alfresco.repo.domain.solr.NodeMetaData; +import org.alfresco.repo.domain.solr.NodeMetaDataParameters; +import org.alfresco.repo.domain.solr.SOLRDAO; +import org.alfresco.repo.domain.solr.SOLRDAO.NodeMetaDataQueryCallback; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.SOLRSerializer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.extensions.surf.util.Content; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +// TODO how to hook into bulk loading of nodes + properties + aspects? +// todo url parameter to remove whitespace in results - make it the default? +public class GetNodesMetaData extends DeclarativeWebScript +{ + protected static final Log logger = LogFactory.getLog(GetNodesMetaData.class); + private static final int INITIAL_DEFAULT_SIZE = 100; + private static final int BATCH_SIZE = 50; + + private SOLRDAO solrDAO; + private SOLRSerializer solrSerializer; + + /** + * @param solrDAO the solrDAO to set + */ + public void setSolrDAO(SOLRDAO solrDAO) + { + this.solrDAO = solrDAO; + } + + public void setSolrSerializer(SOLRSerializer solrSerializer) + { + this.solrSerializer = solrSerializer; + } + + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status) + { + try + { + Content content = req.getContent(); + if(content == null) + { + throw new WebScriptException("Failed to convert request to String"); + } + JSONObject o = new JSONObject(content.getContent()); + + List nodeIds = null; + if(o.has("nodeIds")) + { + JSONArray jsonNodeIds = o.getJSONArray("nodeIds"); + nodeIds = new ArrayList(jsonNodeIds.length()); + for(int i = 0; i < jsonNodeIds.length(); i++) + { + Long nodeId = jsonNodeIds.getLong(i); + nodeIds.add(nodeId); + } + } + + Long fromNodeId = o.has("fromNodeId") ? o.getLong("fromNodeId") : null; + Long toNodeId = o.has("toNodeId") ? o.getLong("toNodeId") : null; + + // 0 or Integer.MAX_VALUE => ignore + int maxResults = o.has("maxResults") ? o.getInt("maxResults") : 0; + + int size = 0; + if(maxResults != 0 && maxResults != Integer.MAX_VALUE) + { + size = maxResults; + } + else if(nodeIds != null) + { + size = nodeIds.size(); + } + else if(fromNodeId != null && toNodeId != null) + { + if((toNodeId.longValue() - fromNodeId.longValue()) > Integer.MAX_VALUE) + { + throw new WebScriptException("Too many nodes expected, try changing the criteria"); + } + size = (int)(toNodeId - fromNodeId); + } + + final boolean noSizeCalculated = (size == 0); + + // filters, defaults are 'true' + MetaDataResultsFilter filter = new MetaDataResultsFilter(); + if(o.has("includeAclId")) + { + filter.setIncludeAclId(o.getBoolean("includeAclId")); + } + if(o.has("includeAspects")) + { + filter.setIncludeAspects(o.getBoolean("includeAspects")); + } + if(o.has("includeNodeRef")) + { + filter.setIncludeNodeRef(o.getBoolean("includeNodeRef")); + } + if(o.has("includeOwner")) + { + filter.setIncludeOwner(o.getBoolean("includeOwner")); + } + if(o.has("includeProperties")) + { + filter.setIncludeProperties(o.getBoolean("includeProperties")); + } + if(o.has("includePaths")) + { + filter.setIncludePaths(o.getBoolean("includePaths")); + } + if(o.has("includeType")) + { + filter.setIncludeType(o.getBoolean("includeType")); + } + if(o.has("includeAssociations")) + { + filter.setIncludeAssociations(o.getBoolean("includeAssociations")); + } + + final ArrayList nodesMetaData = + new ArrayList(size > 0 ? size : INITIAL_DEFAULT_SIZE); + NodeMetaDataParameters params = new NodeMetaDataParameters(); + params.setNodeIds(nodeIds); + params.setFromNodeId(fromNodeId); + params.setToNodeId(toNodeId); + params.setMaxResults(maxResults); + + solrDAO.getNodesMetadata(params, filter, new NodeMetaDataQueryCallback() + { + private int counter = BATCH_SIZE; + private int numBatches = 0; + + public boolean handleNodeMetaData(NodeMetaData nodeMetaData) + { + // need to perform data structure conversions that are compatible with Freemarker + // e.g. Serializable -> String, QName -> String (because map keys must be string, number) + FreemarkerNodeMetaData fNodeMetaData = new FreemarkerNodeMetaData(solrSerializer, nodeMetaData); + nodesMetaData.add(fNodeMetaData); + + if(noSizeCalculated && --counter == 0) + { + counter = BATCH_SIZE; + nodesMetaData.ensureCapacity(++numBatches*BATCH_SIZE); + } + + return true; + } + }); + + Map model = new HashMap(1, 1.0f); + model.put("nodes", nodesMetaData); + model.put("filter", filter); + + if (logger.isDebugEnabled()) + { + logger.debug("Result: \n\tRequest: " + req + "\n\tModel: " + model); + } + + return model; + } + catch(IOException e) + { + throw new WebScriptException("IO exception parsing request", e); + } + catch(JSONException e) + { + throw new WebScriptException("Invalid JSON", e); + } + } + + public static class FreemarkerNodeMetaData + { + private Long nodeId; + private NodeRef nodeRef; + private QName nodeType; + private Long aclId; + private Map properties; + private Set aspects; + private List paths; + private List childAssocs; + + public FreemarkerNodeMetaData(SOLRSerializer solrSerializer, NodeMetaData nodeMetaData) + { + setNodeId(nodeMetaData.getNodeId()); + setAclId(nodeMetaData.getAclId()); + setNodeRef(nodeMetaData.getNodeRef()); + setNodeType(nodeMetaData.getNodeType()); + + // TODO need to use SOLRTypeConverter to serialize Path + for(Path path : nodeMetaData.getPaths()) + { + + } + setPaths(nodeMetaData.getPaths()); + setChildAssocs(nodeMetaData.getChildAssocs()); + setAspects(nodeMetaData.getAspects()); + Map props = nodeMetaData.getProperties(); + Map properties = (props != null ? new HashMap(props.size()) : null); + for(QName propName : props.keySet()) + { + Serializable value = props.get(propName); + + if(value instanceof ContentDataWithId) + { + // special case - ContentDataWithId + properties.put(propName.toString(), ((ContentDataWithId)value).getId()); + } + else + { + properties.put(propName.toString(), solrSerializer.serialize(propName, value)); + } + } + setProperties(properties); + } + + public NodeRef getNodeRef() + { + return nodeRef; + } + public void setNodeRef(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + public List getPaths() + { + return paths; + } + public void setPaths(List paths) + { + this.paths = paths; + } + public QName getNodeType() + { + return nodeType; + } + public void setNodeType(QName nodeType) + { + this.nodeType = nodeType; + } + public Long getNodeId() + { + return nodeId; + } + public void setNodeId(Long nodeId) + { + this.nodeId = nodeId; + } + public Long getAclId() + { + return aclId; + } + public void setAclId(Long aclId) + { + this.aclId = aclId; + } + public Map getProperties() + { + return properties; + } + public void setProperties(Map properties) + { + this.properties = properties; + } + public Set getAspects() + { + return aspects; + } + public void setAspects(Set aspects) + { + this.aspects = aspects; + } + public List getChildAssocs() + { + return childAssocs; + } + public void setChildAssocs(List childAssocs) + { + this.childAssocs = childAssocs; + } + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/solr/GetNodesParameters.java b/source/java/org/alfresco/repo/web/scripts/solr/GetNodesParameters.java new file mode 100644 index 0000000000..216112386b --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/solr/GetNodesParameters.java @@ -0,0 +1,118 @@ +package org.alfresco.repo.web.scripts.solr; + +import java.util.List; +import java.util.Set; + +import org.alfresco.service.namespace.QName; + +public class GetNodesParameters +{ + private List transactionIds; + private Long fromNodeId; + private Long toNodeId; + + private String storeProtocol; + private String storeIdentifier; + + private Set includeNodeTypes; + private Set excludeNodeTypes; + + private Set includeAspects; + private Set excludeAspects; + + public boolean getStoreFilter() + { + return (storeProtocol != null || storeIdentifier != null); + } + + public void setStoreProtocol(String storeProtocol) + { + this.storeProtocol = storeProtocol; + } + + public String getStoreProtocol() + { + return storeProtocol; + } + + public void setStoreIdentifier(String storeIdentifier) + { + this.storeIdentifier = storeIdentifier; + } + + public String getStoreIdentifier() + { + return storeIdentifier; + } + + public void setTransactionIds(List txnIds) + { + this.transactionIds = txnIds; + } + + public List getTransactionIds() + { + return transactionIds; + } + + public Long getFromNodeId() + { + return fromNodeId; + } + + public void setFromNodeId(Long fromNodeId) + { + this.fromNodeId = fromNodeId; + } + + public Long getToNodeId() + { + return toNodeId; + } + + public void setToNodeId(Long toNodeId) + { + this.toNodeId = toNodeId; + } + + public Set getIncludeNodeTypes() + { + return includeNodeTypes; + } + + public Set getExcludeNodeTypes() + { + return excludeNodeTypes; + } + + public Set getIncludeAspects() + { + return includeAspects; + } + + public Set getExcludeAspects() + { + return excludeAspects; + } + + public void setIncludeNodeTypes(Set includeNodeTypes) + { + this.includeNodeTypes = includeNodeTypes; + } + + public void setExcludeNodeTypes(Set excludeNodeTypes) + { + this.excludeNodeTypes = excludeNodeTypes; + } + + public void setIncludeAspects(Set includeAspects) + { + this.includeAspects = includeAspects; + } + + public void setExcludeAspects(Set excludeAspects) + { + this.excludeAspects = excludeAspects; + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/solr/GetTransactions.java b/source/java/org/alfresco/repo/web/scripts/solr/GetTransactions.java index 7be5548aa3..b1c80f3cc6 100644 --- a/source/java/org/alfresco/repo/web/scripts/solr/GetTransactions.java +++ b/source/java/org/alfresco/repo/web/scripts/solr/GetTransactions.java @@ -10,7 +10,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; /** @@ -18,6 +17,8 @@ import org.springframework.extensions.webscripts.WebScriptRequest; * * @since 4.0 */ +// TODO check that this does not return incomplete transactions i.e. commitTimeMs is set - does +// the sql account for this? public class GetTransactions extends DeclarativeWebScript { protected static final Log logger = LogFactory.getLog(GetTransactions.class); diff --git a/source/java/org/alfresco/repo/web/scripts/solr/JSONWriter.java b/source/java/org/alfresco/repo/web/scripts/solr/JSONWriter.java new file mode 100644 index 0000000000..b487548d3d --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/solr/JSONWriter.java @@ -0,0 +1,399 @@ +package org.alfresco.repo.web.scripts.solr; + +import java.io.IOException; +import java.io.Writer; +import java.util.Stack; + +/** + * Fast and simple JSON stream writer. Wraps a Writer to output a JSON object stream. + * No intermediate objects are created - writes are immediate to the underlying stream. + * Quoted and correct JSON encoding is performed on string values, - encoding is + * not performed on key names - it is assumed they are simple strings. The developer must + * call JSONWriter.encodeJSONString() on the key name if required. + * + * @author Kevin Roast + * @author Steve Glover + * + * Adapted from org.springframework.extensions.webscripts.json. + * - added writeValue methods for class versions of primitives + */ +public final class JSONWriter +{ + private Writer out; + private Stack stack = new Stack(); + + /** + * Constructor + * + * @param out The Writer to immediately append values to (no internal buffering) + */ + public JSONWriter(Writer out) + { + this.out = out; + stack.push(Boolean.FALSE); + } + + /** + * Start an array structure, the endArray() method must be called later. + * NOTE: Within the array, either output objects or use the single arg writeValue() method. + */ + public void startArray() throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write("["); + stack.pop(); + stack.push(Boolean.TRUE); + stack.push(Boolean.FALSE); + } + + /** + * End an array structure. + */ + public void endArray() throws IOException + { + out.write("]"); + stack.pop(); + } + + /** + * Start an object structure, the endObject() method must be called later. + */ + public void startObject() throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write("{"); + stack.pop(); + stack.push(Boolean.TRUE); + stack.push(Boolean.FALSE); + } + + /** + * End an object structure. + */ + public void endObject() throws IOException + { + out.write("}"); + stack.pop(); + } + + /** + * Start a value (outputs just a name key), the endValue() method must be called later. + * NOTE: follow with an array or object only. + */ + public void startValue(String name) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + stack.pop(); + stack.push(Boolean.TRUE); + stack.push(Boolean.FALSE); + } + + /** + * End a value that was started with startValue() + */ + public void endValue() + { + stack.pop(); + } + + /** + * Output a JSON string name and value pair. + */ + public void writeValue(String name, String value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": \""); + out.write(encodeJSONString(value)); + out.write('"'); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON number name and value pair. + */ + public void writeValue(String name, int value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(Integer.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(String name, Integer value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON number name and value pair. + */ + public void writeValue(String name, float value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(Float.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(String name, Float value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + + /** + * Output a JSON boolean name and value pair. + */ + public void writeValue(String name, boolean value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(Boolean.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(String name, Boolean value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON string value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeValue(String value) throws IOException + { + if (stack.peek() == true) out.write(","); + out.write('"'); + out.write(encodeJSONString(value)); + out.write('"'); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON number value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeValue(int value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(Integer.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(Integer value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON long value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeValue(long value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(Long.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(Long value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + + /** + * Output a JSON number value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeValue(float value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(Float.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(Float value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON boolean value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeValue(boolean value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(Boolean.toString(value)); + stack.pop(); + stack.push(Boolean.TRUE); + } + + public void writeValue(Boolean value) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write(value.toString()); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON null value. + * NOTE: no name is written - call from within an array structure. + */ + public void writeNullValue() throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write("null"); + stack.pop(); + stack.push(Boolean.TRUE); + } + + /** + * Output a JSON null value. + */ + public void writeNullValue(String name) throws IOException + { + if (stack.peek() == true) out.write(", "); + out.write('"'); + out.write(name); + out.write("\": null"); + stack.pop(); + stack.push(Boolean.TRUE); + } + + + /** + * Safely encode a JSON string value. + * @return encoded string, null is handled and returned as "". + */ + public static String encodeJSONString(final String s) + { + if (s == null || s.length() == 0) + { + return ""; + } + + StringBuilder sb = null; // create on demand + String enc; + char c; + int len = s.length(); + for (int i = 0; i < len; i++) + { + enc = null; + c = s.charAt(i); + switch (c) + { + case '\\': + enc = "\\\\"; + break; + case '"': + enc = "\\\""; + break; + case '/': + enc = "\\/"; + break; + case '\b': + enc = "\\b"; + break; + case '\t': + enc = "\\t"; + break; + case '\n': + enc = "\\n"; + break; + case '\f': + enc = "\\f"; + break; + case '\r': + enc = "\\r"; + break; + + default: + if (((int)c) >= 0x80) + { + //encode all non basic latin characters + String u = "000" + Integer.toHexString((int)c); + enc = "\\u" + u.substring(u.length() - 4); + + } + break; + } + + if (enc != null) + { + if (sb == null) + { + String soFar = s.substring(0, i); + sb = new StringBuilder(i + 8); + sb.append(soFar); + } + sb.append(enc); + } + else + { + if (sb != null) + { + sb.append(c); + } + } + } + + if (sb == null) + { + return s; + } + else + { + return sb.toString(); + } + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/solr/NodeMetaData.java b/source/java/org/alfresco/repo/web/scripts/solr/NodeMetaData.java new file mode 100644 index 0000000000..e97194583a --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/solr/NodeMetaData.java @@ -0,0 +1,76 @@ +package org.alfresco.repo.web.scripts.solr; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.namespace.QName; + +public class NodeMetaData +{ + private long id; + private NodeRef nodeRef; + private QName type; + private long aclId; + private Map properties; + private Set aspects; + private Path paths; + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public NodeRef getNodeRef() + { + return nodeRef; + } + public void setNodeRef(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + public QName getType() + { + return type; + } + public void setType(QName type) + { + this.type = type; + } + public long getAclId() + { + return aclId; + } + public void setAclId(long aclId) + { + this.aclId = aclId; + } + public Map getProperties() + { + return properties; + } + public void setProperties(Map properties) + { + this.properties = properties; + } + public Set getAspects() + { + return aspects; + } + public void setAspects(Set aspects) + { + this.aspects = aspects; + } + public Path getPaths() + { + return paths; + } + public void setPaths(Path paths) + { + this.paths = paths; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/solr/SOLRWebScriptTest.java b/source/java/org/alfresco/repo/web/scripts/solr/SOLRWebScriptTest.java index 839179cd10..d2b57a1f22 100644 --- a/source/java/org/alfresco/repo/web/scripts/solr/SOLRWebScriptTest.java +++ b/source/java/org/alfresco/repo/web/scripts/solr/SOLRWebScriptTest.java @@ -1,11 +1,15 @@ package org.alfresco.repo.web.scripts.solr; import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; @@ -20,7 +24,9 @@ import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.Pair; @@ -55,6 +61,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest private ContentService contentService; private FileFolderService fileFolderService; private RetryingTransactionHelper txnHelper; + private NamespaceService namespaceService; private String admin; @@ -87,8 +94,8 @@ public class SOLRWebScriptTest extends BaseWebScriptTest private JSONObject secondNode; private JSONObject thirdNode; - private NodeRef[][] contents = new NodeRef[10][100]; - private Long[][] nodeIDs = new Long[10][100]; + private ArrayList contents = new ArrayList(100); + private List nodeIDs = new ArrayList(100); @Override protected void setUp() throws Exception @@ -101,6 +108,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest nodeService = serviceRegistry.getNodeService(); contentService = serviceRegistry.getContentService(); fileFolderService = serviceRegistry.getFileFolderService(); + namespaceService = serviceRegistry.getNamespaceService(); txnHelper = transactionService.getRetryingTransactionHelper(); nodeDAO = (NodeDAO)ctx.getBean("nodeDAO"); @@ -121,68 +129,116 @@ public class SOLRWebScriptTest extends BaseWebScriptTest super.tearDown(); } - private JSONArray getNodes(GetNodesParameters parameters, Integer maxResults) throws Exception + private JSONArray getTransactions(long fromCommitTime) throws Exception { - StringBuilder url = new StringBuilder("/api/solr/nodes?txnIds="); - url.append(join(parameters.getTransactionIds(), ",")); - if(parameters.getFromNodeId() != null) - { - url.append("&fromNodeId="); - url.append(parameters.getFromNodeId()); - } - if(parameters.getToNodeId() != null) - { - url.append("&toNodeId="); - url.append(parameters.getToNodeId()); - } - if(parameters.getExcludeAspects() != null) - { - url.append("&excludeAspects="); - Set excludeAspects = parameters.getExcludeAspects(); - int i = 0; - for(QName excludeAspect : excludeAspects) - { - url.append(excludeAspect.toString()); - if(i < (excludeAspects.size() - 1)) - { - url.append(","); - } - i++; - } - } - if(parameters.getIncludeAspects() != null) - { - url.append("&includeAspects="); - Set includeAspects = parameters.getIncludeAspects(); - int i = 0; - for(QName includeAspect : includeAspects) - { - url.append(includeAspect.toString()); - if(i < (includeAspects.size() - 1)) - { - url.append(","); - } - i++; - } - } - if(parameters.getStoreProtocol() != null) - { - url.append("&storeProtocol="); - url.append(parameters.getStoreProtocol()); - } - if(parameters.getStoreIdentifier() != null) - { - url.append("&storeIdentifier="); - url.append(parameters.getStoreIdentifier()); - } - if(maxResults != null) - { - url.append("&maxResults="); - url.append(maxResults); - } + String url = "/api/solr/transactions?fromCommitTime=" + fromCommitTime; + TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url); + long startTime = System.currentTimeMillis(); + Response response = sendRequest(req, Status.STATUS_OK, admin); + long endTime = System.currentTimeMillis(); - TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url.toString()); + if(logger.isDebugEnabled()) + { + logger.debug(response.getContentAsString()); + } + JSONObject json = new JSONObject(response.getContentAsString()); + + JSONArray transactions = json.getJSONArray("transactions"); + + System.out.println("Got " + transactions.length() + " txns in " + (endTime - startTime) + " ms"); + + return transactions; + } + + private JSONArray getNodes(GetNodesParameters parameters, int maxResults, int expectedNumNodes) throws Exception + { + StringBuilder url = new StringBuilder("/api/solr/nodes"); + StringWriter body = new StringWriter(); + JSONWriter jsonOut = new JSONWriter(body); + + jsonOut.startObject(); + { + if(parameters.getTransactionIds() != null) + { + jsonOut.startValue("txnIds"); + { + jsonOut.startArray(); + { + for(Long txnId : parameters.getTransactionIds()) + { + jsonOut.writeValue(txnId); + } + + } + jsonOut.endArray(); + } + jsonOut.endValue(); + } + + if(parameters.getFromNodeId() != null) + { + jsonOut.writeValue("fromNodeId", parameters.getFromNodeId()); + } + if(parameters.getToNodeId() != null) + { + jsonOut.writeValue("toNodeId", parameters.getToNodeId()); + } + if(parameters.getExcludeAspects() != null) + { + jsonOut.startValue("excludeAspects"); + { + jsonOut.startArray(); + { + for(QName excludeAspect : parameters.getExcludeAspects()) + { + jsonOut.writeValue(excludeAspect.toString()); + } + } + jsonOut.endArray(); + } + jsonOut.endValue(); + } + if(parameters.getIncludeAspects() != null) + { + jsonOut.startValue("includeAspects"); + { + jsonOut.startArray(); + { + for(QName includeAspect : parameters.getIncludeAspects()) + { + jsonOut.writeValue(includeAspect.toString()); + } + } + jsonOut.endArray(); + } + jsonOut.endValue(); + } + + if(parameters.getStoreProtocol() != null) + { + jsonOut.writeValue("storeProtocol", parameters.getStoreProtocol()); + } + + if(parameters.getStoreIdentifier() != null) + { + jsonOut.writeValue("storeIdentifier", parameters.getStoreIdentifier()); + } + + jsonOut.writeValue("maxResults", maxResults); + } + jsonOut.endObject(); + +// if(maxResults != null) +// { +// url.append("&maxResults="); +// url.append(maxResults); +// } + + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url.toString(), body.toString(), "application/json"); + + long startTime = System.currentTimeMillis(); Response response = sendRequest(req, Status.STATUS_OK, admin); + long endTime = System.currentTimeMillis(); // assertEquals("Expected application/json content type", "application/json[;charset=UTF-8]", response.getContentType()); @@ -190,13 +246,17 @@ public class SOLRWebScriptTest extends BaseWebScriptTest { logger.debug(response.getContentAsString()); } - + //System.out.println("getNodes: " + response.getContentAsString()); JSONObject json = new JSONObject(response.getContentAsString()); json.write(new PrintWriter(System.out)); JSONArray nodes = json.getJSONArray("nodes"); - assertEquals("Node count is incorrect", nodes.length(), json.getInt("count")); + //assertEquals("Node count is incorrect", nodes.length(), json.getInt("count")); + + System.out.println("Got " + nodes.length() + " nodes in " + (endTime - startTime) + " ms"); + + assertEquals("Number of returned node meta data results is incorrect", expectedNumNodes, nodes.length()); return nodes; } @@ -289,10 +349,10 @@ public class SOLRWebScriptTest extends BaseWebScriptTest String url = "/api/solr/transactions?fromCommitTime=" + fromCommitTime; TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url); Response response = sendRequest(req, Status.STATUS_OK, admin); - + // assertEquals("Expected application/json content type", "application/json[;charset=UTF-8]", response.getContentType()); - System.out.println(response.getContentAsString()); +// System.out.println(response.getContentAsString()); JSONObject json = new JSONObject(response.getContentAsString()); JSONArray transactions = json.getJSONArray("transactions"); @@ -300,7 +360,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest int[] updates = new int[] {1, 1}; int[] deletes = new int[] {0, 1}; - StringBuilder txnIds = new StringBuilder(); + //StringBuilder txnIds = new StringBuilder(); int numTxns = transactions.length(); List transactionIds = getTransactionIds(transactions); for(int i = 0; i < numTxns; i++) @@ -319,22 +379,21 @@ public class SOLRWebScriptTest extends BaseWebScriptTest // get all nodes at once if(logger.isDebugEnabled()) { - logger.debug("txnIds = " + txnIds.toString()); + logger.debug("txnIds = " + transactions.toString()); } GetNodesParameters parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); - JSONArray nodes = getNodes(parameters, null); + JSONArray nodes = getNodes(parameters, 0, 3); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 3, nodes.length()); JSONObject lastNode = nodes.getJSONObject(2); - assertTrue("nodeID is missing", lastNode.has("nodeID")); - Long fromNodeId = lastNode.getLong("nodeID"); + assertTrue("nodeID is missing", lastNode.has("id")); + Long fromNodeId = lastNode.getLong("id"); if(logger.isDebugEnabled()) { logger.debug("fromNodeId = " + fromNodeId); @@ -344,25 +403,24 @@ public class SOLRWebScriptTest extends BaseWebScriptTest firstNode = nodes.getJSONObject(0); secondNode = nodes.getJSONObject(1); //assertEquals("Expected transaction ids to be the same", firstNode.getLong("txnID") == secondNode.getLong("txnID")); - assertEquals("Expected node update", false, firstNode.getBoolean("deleted")); - assertEquals("Expected node deleted", true, secondNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("nodeID")); - assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("nodeID")); + assertEquals("Expected node update", "u", firstNode.getString("status")); + assertEquals("Expected node deleted", "d", secondNode.getString("status")); + assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("id")); + assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("id")); // get first 2 nodes parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); - nodes = getNodes(parameters, 2); + nodes = getNodes(parameters, 2, 2); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 2, nodes.length()); lastNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", lastNode.has("nodeID")); - fromNodeId = lastNode.getLong("nodeID"); + assertTrue("nodeID is missing", lastNode.has("id")); + fromNodeId = lastNode.getLong("id"); if(logger.isDebugEnabled()) { logger.debug("fromNodeId = " + fromNodeId); @@ -373,96 +431,91 @@ public class SOLRWebScriptTest extends BaseWebScriptTest parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setFromNodeId(fromNodeId); - nodes = getNodes(parameters, 4); + nodes = getNodes(parameters, 4, 2); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 2, nodes.length()); firstNode = nodes.getJSONObject(0); secondNode = nodes.getJSONObject(1); - assertEquals("Expected node deleted", true, firstNode.getBoolean("deleted")); - assertEquals("Expected node updated", false, secondNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", content1NodeID, firstNode.getLong("nodeID")); - assertEquals("Node id is incorrect", content2NodeID, secondNode.getLong("nodeID")); + assertEquals("Expected node deleted", "d", firstNode.getString("status")); + assertEquals("Expected node updated", "u", secondNode.getString("status")); + assertEquals("Node id is incorrect", content1NodeID, firstNode.getLong("id")); + assertEquals("Node id is incorrect", content2NodeID, secondNode.getLong("id")); // get 0 (all) nodes starting with fromNodeId, should return 2 nodes parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setFromNodeId(fromNodeId); - nodes = getNodes(parameters, 0); + nodes = getNodes(parameters, 0, 2); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 2, nodes.length()); // get 2 nodes ending with toNodeId, should return 2 nodes long toNodeId = content2NodeID; parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setToNodeId(toNodeId); - nodes = getNodes(parameters, 2); + nodes = getNodes(parameters, 2, 2); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 2, nodes.length()); firstNode = nodes.getJSONObject(0); secondNode = nodes.getJSONObject(1); - assertEquals("Expected node deleted", false, firstNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("nodeID")); - assertEquals("Expected node updated", true, secondNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("nodeID")); + assertEquals("Expected node deleted", "u", firstNode.getString("status")); + assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("id")); + assertEquals("Expected node updated", "d", secondNode.getString("status")); + assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("id")); // get 1 node ending with toNodeId, should return 1 nodes toNodeId = content2NodeID; parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setToNodeId(toNodeId); - nodes = getNodes(parameters, 1); + nodes = getNodes(parameters, 1, 1); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 1, nodes.length()); firstNode = nodes.getJSONObject(0); - assertEquals("Expected node updated", false, firstNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("nodeID")); + assertEquals("Expected node updated", "u", firstNode.getString("status")); + assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("id")); // get 3 nodes ending with toNodeId, should return 3 nodes toNodeId = content2NodeID; parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setToNodeId(toNodeId); - nodes = getNodes(parameters, 3); + nodes = getNodes(parameters, 3, 3); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 3, nodes.length()); firstNode = nodes.getJSONObject(0); - assertEquals("Expected node updated", false, firstNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("nodeID")); + assertEquals("Expected node updated", "u", firstNode.getString("status")); + assertEquals("Node id is incorrect", container1NodeID, firstNode.getLong("id")); secondNode = nodes.getJSONObject(1); - assertEquals("Expected node deleted", true, secondNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("nodeID")); + assertEquals("Expected node deleted", "d", secondNode.getString("status")); + assertEquals("Node id is incorrect", content1NodeID, secondNode.getLong("id")); thirdNode = nodes.getJSONObject(2); - assertEquals("Expected node updated", false, thirdNode.getBoolean("deleted")); - assertEquals("Node id is incorrect", content2NodeID, thirdNode.getLong("nodeID")); + assertEquals("Expected node updated", "u", thirdNode.getString("status")); + assertEquals("Node id is incorrect", content2NodeID, thirdNode.getLong("id")); } - + private void buildTransactions2() { txnHelper.doInTransaction(new RetryingTransactionCallback() @@ -538,6 +591,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest if(logger.isDebugEnabled()) { + logger.debug("txns ="); logger.debug(response.getContentAsString()); } JSONObject json = new JSONObject(response.getContentAsString()); @@ -575,28 +629,26 @@ public class SOLRWebScriptTest extends BaseWebScriptTest // get all nodes in the txns GetNodesParameters parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); - JSONArray nodes = getNodes(parameters, null); - assertEquals("Number of nodes is incorrect", 4, nodes.length()); + JSONArray nodes = getNodes(parameters, 0, 4); JSONObject lastNode = nodes.getJSONObject(nodes.length() - 1); - Long fromNodeId = lastNode.getLong("nodeID"); + Long fromNodeId = lastNode.getLong("id"); assertNotNull("Unexpected null fromNodeId", fromNodeId); // get first 2 nodes parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); - nodes = getNodes(parameters, 2); + nodes = getNodes(parameters, 2, 2); if(logger.isDebugEnabled()) { logger.debug("nodes:"); logger.debug(nodes.toString(3)); } - assertEquals("Number of nodes is incorrect", 2, nodes.length()); firstNode = nodes.getJSONObject(0); - assertTrue("nodeID is missing", firstNode.has("nodeID")); + assertTrue("nodeID is missing", firstNode.has("id")); secondNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", secondNode.has("nodeID")); - fromNodeId = secondNode.getLong("nodeID"); + assertTrue("nodeID is missing", secondNode.has("id")); + fromNodeId = secondNode.getLong("id"); if(logger.isDebugEnabled()) { logger.debug("fromNodeId = " + fromNodeId); @@ -604,74 +656,70 @@ public class SOLRWebScriptTest extends BaseWebScriptTest assertNotNull("Unexpected null nodeID", fromNodeId); //assertEquals("Expected transaction ids to be the same", firstNode.getLong("txnID"), secondNode.getLong("txnID")); - assertEquals("Expected node update", false, firstNode.getBoolean("deleted")); - assertEquals("Expected node delete", true, secondNode.getBoolean("deleted")); - assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("nodeID")); - assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("nodeID")); + assertEquals("Expected node update", "u", firstNode.getString("status")); + assertEquals("Expected node delete", "d", secondNode.getString("status")); + assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("id")); + assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("id")); // get 10 nodes (including fromNodeId) starting with fromNodeId, should return only 3 nodes parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setFromNodeId(fromNodeId); - nodes = getNodes(parameters, 10); - assertEquals("Number of nodes is incorrect", 3, nodes.length()); + nodes = getNodes(parameters, 10, 3); firstNode = nodes.getJSONObject(0); - assertTrue("nodeID is missing", firstNode.has("nodeID")); + assertTrue("nodeID is missing", firstNode.has("id")); secondNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", secondNode.has("nodeID")); + assertTrue("nodeID is missing", secondNode.has("id")); thirdNode = nodes.getJSONObject(2); - assertTrue("nodeID is missing", thirdNode.has("nodeID")); + assertTrue("nodeID is missing", thirdNode.has("id")); - assertEquals("Expected node delete", true, firstNode.getBoolean("deleted")); - assertEquals("Expected node update", false, secondNode.getBoolean("deleted")); - assertEquals("Expected node update", false, thirdNode.getBoolean("deleted")); - assertEquals("Incorrect node id", content1NodeID, firstNode.getLong("nodeID")); - assertEquals("Incorrect node id", content2NodeID, secondNode.getLong("nodeID")); - assertEquals("Incorrect node id", content3NodeID, thirdNode.getLong("nodeID")); + assertEquals("Expected node delete", "d", firstNode.getString("status")); + assertEquals("Expected node update", "u", secondNode.getString("status")); + assertEquals("Expected node update", "u", thirdNode.getString("status")); + assertEquals("Incorrect node id", content1NodeID, firstNode.getLong("id")); + assertEquals("Incorrect node id", content2NodeID, secondNode.getLong("id")); + assertEquals("Incorrect node id", content3NodeID, thirdNode.getLong("id")); // test with from and to node ids parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setFromNodeId(container2NodeID); parameters.setToNodeId(content3NodeID); - nodes = getNodes(parameters, 2); - assertEquals("Number of nodes is incorrect", 2, nodes.length()); + nodes = getNodes(parameters, 2, 2); firstNode = nodes.getJSONObject(0); - assertTrue("nodeID is missing", firstNode.has("nodeID")); + assertTrue("nodeID is missing", firstNode.has("id")); secondNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", secondNode.has("nodeID")); - assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("nodeID")); - assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("nodeID")); + assertTrue("nodeID is missing", secondNode.has("id")); + assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("id")); + assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("id")); // test right truncation parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setToNodeId(content3NodeID); - nodes = getNodes(parameters, 2); - assertEquals("Number of nodes is incorrect", 2, nodes.length()); + nodes = getNodes(parameters, 2, 2); firstNode = nodes.getJSONObject(0); - assertTrue("nodeID is missing", firstNode.has("nodeID")); + assertTrue("nodeID is missing", firstNode.has("id")); secondNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", secondNode.has("nodeID")); - assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("nodeID")); - assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("nodeID")); + assertTrue("nodeID is missing", secondNode.has("id")); + assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("id")); + assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("id")); // test left truncation, specifying from node only parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setFromNodeId(container2NodeID); - nodes = getNodes(parameters, 2); - assertEquals("Number of nodes is incorrect", 2, nodes.length()); + nodes = getNodes(parameters, 2, 2); firstNode = nodes.getJSONObject(0); - assertTrue("nodeID is missing", firstNode.has("nodeID")); + assertTrue("nodeID is missing", firstNode.has("id")); secondNode = nodes.getJSONObject(1); - assertTrue("nodeID is missing", secondNode.has("nodeID")); - assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("nodeID")); - assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("nodeID")); + assertTrue("nodeID is missing", secondNode.has("id")); + assertEquals("Incorrect node id", container2NodeID, firstNode.getLong("id")); + assertEquals("Incorrect node id", content1NodeID, secondNode.getLong("id")); } private void buildTransactions3() @@ -697,12 +745,13 @@ public class SOLRWebScriptTest extends BaseWebScriptTest for(int i = 0; i < 100; i++) { FileInfo content1Info = fileFolderService.create(container3, "Content" + i, ContentModel.TYPE_CONTENT); - contents[3][i] = content1Info.getNodeRef(); - nodeIDs[3][i] = getNodeID(contents[3][i]); + NodeRef nodeRef = content1Info.getNodeRef(); + contents.add(nodeRef); + nodeIDs.add(Long.valueOf(getNodeID(nodeRef))); if(i % 2 == 1) { - nodeService.addAspect(contents[3][i], ContentModel.ASPECT_TEMPORARY, null); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); } } @@ -723,6 +772,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest if(logger.isDebugEnabled()) { + logger.debug("txns = "); logger.debug(response.getContentAsString()); } JSONObject json = new JSONObject(response.getContentAsString()); @@ -742,8 +792,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest GetNodesParameters parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setExcludeAspects(excludeAspects); - JSONArray nodes = getNodes(parameters, 0); - assertEquals("Number of nodes is incorrect", 51, nodes.length()); + JSONArray nodes = getNodes(parameters, 0, 51); } private void buildTransactions4() @@ -769,12 +818,13 @@ public class SOLRWebScriptTest extends BaseWebScriptTest for(int i = 0; i < 100; i++) { FileInfo content1Info = fileFolderService.create(container4, "Content" + i, ContentModel.TYPE_CONTENT); - contents[4][i] = content1Info.getNodeRef(); - nodeIDs[4][i] = getNodeID(contents[4][i]); + NodeRef nodeRef = content1Info.getNodeRef(); + contents.add(nodeRef); + nodeIDs.add(getNodeID(nodeRef)); if(i % 2 == 1) { - nodeService.addAspect(contents[4][i], ContentModel.ASPECT_TEMPORARY, null); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); } } @@ -803,12 +853,13 @@ public class SOLRWebScriptTest extends BaseWebScriptTest for(int i = 0; i < 100; i++) { FileInfo content1Info = fileFolderService.create(container5, "Content" + i, ContentModel.TYPE_CONTENT); - contents[5][i] = content1Info.getNodeRef(); - nodeIDs[5][i] = getNodeID(contents[5][i]); + NodeRef nodeRef = content1Info.getNodeRef(); + contents.add(nodeRef); + nodeIDs.add(getNodeID(nodeRef)); if(i % 2 == 1) { - nodeService.addAspect(contents[5][i], ContentModel.ASPECT_TEMPORARY, null); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); } } @@ -816,7 +867,7 @@ public class SOLRWebScriptTest extends BaseWebScriptTest } }); } - + public void testGetNodesStoreName() throws Exception { long fromCommitTime = System.currentTimeMillis(); @@ -846,25 +897,304 @@ public class SOLRWebScriptTest extends BaseWebScriptTest parameters.setTransactionIds(transactionIds); parameters.setStoreProtocol(storeRef.getProtocol()); parameters.setStoreIdentifier(storeRef.getIdentifier()); - JSONArray nodes = getNodes(parameters, 0); - assertEquals("Number of nodes is incorrect", 101, nodes.length()); + JSONArray nodes = getNodes(parameters, 0, 101); - nodes = getNodes(parameters, 50); - assertEquals("Number of nodes is incorrect", 50, nodes.length()); + nodes = getNodes(parameters, 50, 50); // store protocol parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setStoreProtocol(storeRef.getProtocol()); - nodes = getNodes(parameters, 0); - assertEquals("Number of nodes is incorrect", 202, nodes.length()); + nodes = getNodes(parameters, 0, 202); // store identifier parameters = new GetNodesParameters(); parameters.setTransactionIds(transactionIds); parameters.setStoreIdentifier(storeRef.getIdentifier()); - nodes = getNodes(parameters, 0); - assertEquals("Number of nodes is incorrect", 101, nodes.length()); + nodes = getNodes(parameters, 0, 101); + } + + private NodeRef container6; + + private void buildTransactions5() + { + txnHelper.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + PropertyMap props = new PropertyMap(); + props.put(ContentModel.PROP_NAME, "Container6"); + container6 = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_FOLDER, + props).getChildRef(); + + FileInfo contentInfo = fileFolderService.create(container6, "Content1", ContentModel.TYPE_CONTENT); + contents.add(contentInfo.getNodeRef()); + + Map aspectProperties = new HashMap(); + aspectProperties.put(ContentModel.PROP_AUTHOR, "steve"); + nodeService.addAspect(contentInfo.getNodeRef(), ContentModel.ASPECT_AUTHOR, aspectProperties); + + return null; + } + }); + } + + private JSONArray getNodesMetaData(List nodeIds, int maxResults, int numMetaDataNodes) throws Exception + { + StringBuilder url = new StringBuilder("/api/solr/metadata"); + StringWriter body = new StringWriter(); + JSONWriter jsonOut = new JSONWriter(body); + + jsonOut.startObject(); + { + if(nodeIds != null && nodeIds.size() > 0) + { + jsonOut.startValue("nodeIds"); + { + jsonOut.startArray(); + for(Long nodeId : nodeIds) + { + jsonOut.writeValue(nodeId); + } + jsonOut.endArray(); + } + jsonOut.endValue(); + } + + jsonOut.writeValue("maxResults", maxResults); + } + jsonOut.endObject(); + + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url.toString(), body.toString(), "application/json"); + long startTime = System.currentTimeMillis(); + Response response = sendRequest(req, Status.STATUS_OK, admin); + long endTime = System.currentTimeMillis(); + + String content = response.getContentAsString(); + + if(logger.isDebugEnabled()) + { + logger.debug("nodesMetaData = " + content); + } + + JSONObject json = new JSONObject(content); + + JSONArray nodes = json.getJSONArray("nodes"); + + System.out.println("Got metadata for " + nodes.length() + " nodes in " + (endTime - startTime) + " ms"); + + assertEquals("Number of returned nodes is incorrect", numMetaDataNodes, nodes.length()); + + return nodes; + } + + public void testNodeMetaData() throws Exception + { + long fromCommitTime = System.currentTimeMillis(); + + buildTransactions5(); + + JSONArray transactions = getTransactions(fromCommitTime); + assertEquals("Number of transactions is incorrect", 1, transactions.length()); + + List transactionIds = getTransactionIds(transactions); + + GetNodesParameters params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + JSONArray nodes = getNodes(params, 0, 2); + + List nodeIds = new ArrayList(nodes.length()); + for(int i = 0; i < nodes.length(); i++) + { + JSONObject node = nodes.getJSONObject(i); + nodeIds.add(node.getLong("id")); + } + + JSONArray nodesMetaData = getNodesMetaData(nodeIds, 0, 2); + + // test second entry (second node created in buildTransactions) + NodeRef expectedNodeRef = contents.get(0); + + JSONObject node = nodesMetaData.getJSONObject(1); + NodeRef nodeRef = new NodeRef(node.getString("nodeRef")); + + assertEquals("NodeRef is incorrect", expectedNodeRef, nodeRef); + + JSONArray aspects = node.getJSONArray("aspects"); + JSONObject properties = node.getJSONObject("properties"); + Map propertyMap = getPropertyMap(properties); + +// assertEquals("Incorrect number of aspects", 1, aspects.length()); + assertTrue("Expected author aspect", containsAspect(aspects, ContentModel.ASPECT_AUTHOR)); + assertTrue("Expected author property", containsProperty(propertyMap, ContentModel.PROP_AUTHOR, "steve")); + + JSONArray paths = node.getJSONArray("paths"); + List expectedPaths = nodeService.getPaths(expectedNodeRef, false); + for(int i = 0; i < paths.length(); i++) + { + String path = paths.getString(i); + String expectedPath = expectedPaths.get(i).toString(); + assertEquals("Path element " + i + " is incorrect", expectedPath, path); + } + + + //assertEquals("Node id is incorrect", containsProperty(properties, ContentModel.PROP_AUTHOR, "steve")); + } + + private void buildTransactions6() + { + txnHelper.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + PropertyMap props = new PropertyMap(); + props.put(ContentModel.PROP_NAME, "Container6"); + NodeRef container6 = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_FOLDER, + props).getChildRef(); + long container6NodeID = getNodeID(container6); + if(logger.isDebugEnabled()) + { + logger.debug("container6 = " + container6); + } + + for(int i = 0; i < 2000; i++) + { + FileInfo content1Info = fileFolderService.create(container6, "Content" + i, ContentModel.TYPE_CONTENT); + NodeRef nodeRef = content1Info.getNodeRef(); + contents.add(nodeRef); + + if(i % 2 == 1) + { + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); + } + } + + return null; + } + }); + } + + public void testNodeMetaDataManyNodes() throws Exception + { + long fromCommitTime = System.currentTimeMillis(); + + buildTransactions6(); + + JSONArray transactions = getTransactions(fromCommitTime); + assertEquals("Number of transactions is incorrect", 1, transactions.length()); + + List transactionIds = getTransactionIds(transactions); + + GetNodesParameters params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + JSONArray nodes = getNodes(params, 0, 2001); + + List nodeIds = new ArrayList(nodes.length()); + for(int i = 0; i < nodes.length(); i++) + { + JSONObject node = nodes.getJSONObject(i); + nodeIds.add(node.getLong("id")); + } + + // make sure caches are warm - time last call + JSONArray nodesMetaData = getNodesMetaData(nodeIds, 0, 2001); + nodesMetaData = getNodesMetaData(nodeIds, 0, 2001); + + // sleep for a couple of seconds + try + { + Thread.sleep(2000); + } + catch(InterruptedException e) + { + // ignore + } + nodesMetaData = getNodesMetaData(nodeIds, 0, 2001); + + nodesMetaData = getNodesMetaData(nodeIds, 1000, 1000); + nodesMetaData = getNodesMetaData(nodeIds, 600, 600); + nodesMetaData = getNodesMetaData(nodeIds, 300, 300); + nodesMetaData = getNodesMetaData(nodeIds, 100, 100); + nodesMetaData = getNodesMetaData(nodeIds, 50, 50); + + // clear out caches + nodeDAO.clear(); + + nodesMetaData = getNodesMetaData(nodeIds, 0, 2001); + } + + private boolean containsAspect(JSONArray aspectsArray, QName aspect) throws Exception + { + if(aspect == null) + { + throw new IllegalArgumentException("aspect cannot be null"); + } + + boolean success = false; + for(int i = 0; i < aspectsArray.length(); i++) + { + String qName = aspectsArray.getString(i); + if(aspect.equals(QName.createQName(qName, namespaceService))) + { + success |= true; + break; + } + } + + return success; + } + + private Map getPropertyMap(JSONObject properties) throws Exception + { + Map propertyMap = new HashMap(properties.length()); + @SuppressWarnings("rawtypes") + Iterator propNames = properties.keys(); + while(propNames.hasNext()) + { + String propName = (String)propNames.next(); + String value = properties.getString(propName); + + propertyMap.put(QName.createQName(propName, namespaceService), value); + } + + return propertyMap; + } + + private boolean containsProperty(Map propertyMap, QName propName, String propValue) throws Exception + { + if(propName == null) + { + throw new IllegalArgumentException("propName cannot be null"); + } + + String value = propertyMap.get(propName); + return (value == null ? false : value.equals(propValue)); +// boolean success = false; +// for(int i = 0; i < propertiesArray.length(); i++) +// { +// JSONObject prop = propertiesArray.getJSONObject(i); +// prop.keys(); +// String qName = prop.getString("name"); +// String value = prop.getString("value"); +// if(qName.equals(QName.createQName(qName))) +// { +// success |= (propValue == null ? true : value.equals(propValue)); +// } +// if(success) +// { +// break; +// } +// } +// +// return success; } /* private void buildTransactions3() @@ -928,116 +1258,4 @@ public class SOLRWebScriptTest extends BaseWebScriptTest assertNotNull("Can't find node " + nodeRef, pair); return pair.getFirst(); } - - private static class GetNodesParameters - { - private List transactionIds; - private Long fromNodeId; - private Long toNodeId; - - private String storeProtocol; - private String storeIdentifier; - - private Set includeNodeTypes; - private Set excludeNodeTypes; - - private Set includeAspects; - private Set excludeAspects; - - public boolean getStoreFilter() - { - return (storeProtocol != null || storeIdentifier != null); - } - - public void setStoreProtocol(String storeProtocol) - { - this.storeProtocol = storeProtocol; - } - - public String getStoreProtocol() - { - return storeProtocol; - } - - public void setStoreIdentifier(String storeIdentifier) - { - this.storeIdentifier = storeIdentifier; - } - - public String getStoreIdentifier() - { - return storeIdentifier; - } - - public void setTransactionIds(List txnIds) - { - this.transactionIds = txnIds; - } - - public List getTransactionIds() - { - return transactionIds; - } - - public Long getFromNodeId() - { - return fromNodeId; - } - - public void setFromNodeId(Long fromNodeId) - { - this.fromNodeId = fromNodeId; - } - - public Long getToNodeId() - { - return toNodeId; - } - - public void setToNodeId(Long toNodeId) - { - this.toNodeId = toNodeId; - } - - public Set getIncludeNodeTypes() - { - return includeNodeTypes; - } - - public Set getExcludeNodeTypes() - { - return excludeNodeTypes; - } - - public Set getIncludeAspects() - { - return includeAspects; - } - - public Set getExcludeAspects() - { - return excludeAspects; - } - - public void setIncludeNodeTypes(Set includeNodeTypes) - { - this.includeNodeTypes = includeNodeTypes; - } - - public void setExcludeNodeTypes(Set excludeNodeTypes) - { - this.excludeNodeTypes = excludeNodeTypes; - } - - public void setIncludeAspects(Set includeAspects) - { - this.includeAspects = includeAspects; - } - - public void setExcludeAspects(Set excludeAspects) - { - this.excludeAspects = excludeAspects; - } - - } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/solr/test/SOLRSerializerTests.java b/source/java/org/alfresco/repo/web/scripts/solr/test/SOLRSerializerTests.java new file mode 100644 index 0000000000..f3f0315fd3 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/solr/test/SOLRSerializerTests.java @@ -0,0 +1,257 @@ +package org.alfresco.repo.web.scripts.solr.test; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.node.ContentDataWithId; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.admin.RepoAdminService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +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.Path; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyMap; +import org.alfresco.util.SOLRDeserializer; +import org.alfresco.util.SOLRSerializer; + +public class SOLRSerializerTests extends BaseWebScriptTest +{ + static final String SOLR_TEST_MODEL_1_0_URI = "http://www.alfresco.org/model/solrtest/1.0"; + static final QName TYPE_TEST_OBJECT = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "testobject"); + static final QName PROP_MLTEXT = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "mlTextProp"); + static final QName PROP_BOOL = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "boolProp"); + static final QName PROP_LONG = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "longProp"); + static final QName PROP_FLOAT = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "floatProp"); + static final QName PROP_DOUBLE = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "doubleProp"); + static final QName PROP_DATE = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "dateProp"); + static final QName PROP_DATETIME = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "dateTimeProp"); + static final QName PROP_QNAME = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "qnameProp"); + static final QName PROP_NODEREF = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "nodeRefProp"); + static final QName PROP_CHILDASSOC = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "childAssocProp"); + static final QName PROP_ASSOC = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "assocProp"); + static final QName PROP_PATH = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "pathProp"); + static final QName PROP_CATEGORY = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "categoryProp"); + static final QName PROP_LOCALE = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "localeProp"); + static final QName PROP_PERIOD = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "periodProp"); + static final QName PROP_ANY = QName.createQName(SOLR_TEST_MODEL_1_0_URI, "anyProp"); + + private AuthenticationComponent authenticationComponent; + private TransactionService transactionService; + private RetryingTransactionHelper txnHelper; + private NodeService nodeService; + private FileFolderService fileFolderService; + private ContentService contentService; + private DictionaryService dictionaryService; + private RepoAdminService repoAdminService; + + private SOLRSerializer solrSerializer; + private SOLRDeserializer solrDeserializer; + + private StoreRef storeRef; + private NodeRef rootNodeRef; + + @Override + public void setUp() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) getServer().getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); + transactionService = serviceRegistry.getTransactionService(); + txnHelper = transactionService.getRetryingTransactionHelper(); + fileFolderService = serviceRegistry.getFileFolderService(); + contentService = serviceRegistry.getContentService(); + nodeService = serviceRegistry.getNodeService(); + dictionaryService = serviceRegistry.getDictionaryService(); + authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + repoAdminService = (RepoAdminService)getServer().getApplicationContext().getBean("repoAdminService"); + + solrSerializer = (SOLRSerializer)getServer().getApplicationContext().getBean("solrSerializer"); + solrDeserializer = new SOLRDeserializer(dictionaryService); + + authenticationComponent.setSystemUserAsCurrentUser(); + + InputStream modelStream = getClass().getClassLoader().getResourceAsStream("solr/solr-test-model.xml"); + repoAdminService.deployModel(modelStream, "solr-test-model"); + + storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + System.currentTimeMillis()); + rootNodeRef = nodeService.getRootNode(storeRef); + } + + private NodeRef solrNode; + private Date date; + private MLText mlText; + private ChildAssociationRef childAssoc; + private AssociationRef assoc; + private List multiCategory; + private NodeRef category; + + private static String[] mlOrderable_en = new String[] { "AAAA BBBB", "EEEE FFFF", "II", "KK", "MM", "OO", "QQ", "SS", "UU", "AA", "CC" }; + + private static String[] mlOrderable_fr = new String[] { "CCCC DDDD", "GGGG HHHH", "JJ", "LL", "NN", "PP", "RR", "TT", "VV", "BB", "DD" }; + + private MLText makeMLText() + { + return makeMLText(0); + } + + private MLText makeMLText(int position) + { + MLText ml = new MLText(); + ml.addValue(Locale.ENGLISH, mlOrderable_en[position]); + ml.addValue(Locale.FRENCH, mlOrderable_fr[position]); + return ml; + } + + private void buildTransaction() + { + PropertyMap props = new PropertyMap(); + props.put(ContentModel.PROP_NAME, "Container1"); + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_FOLDER, + props).getChildRef(); + + FileInfo contentInfo = fileFolderService.create(container, "SolrNode", TYPE_TEST_OBJECT); + solrNode = contentInfo.getNodeRef(); + ContentWriter writer = contentService.getWriter(solrNode, ContentModel.PROP_CONTENT, true); + writer.putContent("Some Content"); + + date = new Date(); + mlText = makeMLText(); + childAssoc = new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, + new NodeRef("testProtocol", "testID", "abcde"), + QName.createQName("testProtocol", "testID"), + new NodeRef("testProtocol", "testID", "xyz")); + assoc = new AssociationRef( + new NodeRef("testProtocol", "testID", "abcde"), + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "parts"), + new NodeRef("testProtocol", "testID", "xyz")); + + Map properties = new HashMap(); + properties.put(PROP_BOOL, Boolean.TRUE); + properties.put(PROP_LONG, Long.valueOf(42)); + properties.put(PROP_FLOAT, Float.valueOf(42.0f)); + properties.put(PROP_DOUBLE, Double.valueOf(42.0)); + properties.put(PROP_DATE, date); + properties.put(PROP_DATETIME, date); + properties.put(PROP_NODEREF, container); + properties.put(PROP_LOCALE, Locale.ITALY); + properties.put(PROP_QNAME, PROP_QNAME); + //properties.put(PROP_VERSION, new VersionNumber("1.0")); + properties.put(PROP_PERIOD, new Period("period|12")); + Path path = new Path(); + Path.Element element0 = new Path.ChildAssocElement(new ChildAssociationRef(null, null, null, new NodeRef("testProtocol", "testID", "abcde"))); + path.prepend(element0); + properties.put(PROP_PATH, path); + properties.put(PROP_ASSOC, assoc); + category = new NodeRef("testProtocol", "testID", "cat1"); + properties.put(PROP_CATEGORY, (Serializable)category); + properties.put(PROP_CHILDASSOC, childAssoc); + properties.put(PROP_MLTEXT, mlText); + + nodeService.setProperties(solrNode, properties); + } + + public void testAll() + { + txnHelper.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + buildTransaction(); + + Serializable value = nodeService.getProperty(solrNode, ContentModel.PROP_NAME); + Object serialized = solrSerializer.serialize(ContentModel.PROP_NAME, value); + Serializable deserialized = solrDeserializer.deserialize(ContentModel.PROP_NAME, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_MLTEXT); + serialized = solrSerializer.serialize(PROP_MLTEXT, value); + deserialized = solrDeserializer.deserialize(PROP_MLTEXT, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, ContentModel.PROP_CONTENT); + assertTrue("Expected ContentDataId, got " + value.getClass().getName(), value instanceof ContentDataWithId); + serialized = solrSerializer.serialize(ContentModel.PROP_CONTENT, value); + deserialized = solrDeserializer.deserialize(ContentModel.PROP_CONTENT, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_BOOL); + serialized = solrSerializer.serialize(PROP_BOOL, value); + deserialized = solrDeserializer.deserialize(PROP_BOOL, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_DATE); + assertTrue("Expected Date object, got " + value.getClass().getName(), value instanceof Date); + serialized = solrSerializer.serialize(PROP_DATE, value); + deserialized = solrDeserializer.deserialize(PROP_DATE, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_DATETIME); + assertTrue("Expected Date object, got " + value.getClass().getName(), value instanceof Date); + serialized = solrSerializer.serialize(PROP_DATETIME, value); + deserialized = solrDeserializer.deserialize(PROP_DATETIME, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_DOUBLE); + assertTrue("Expected Double object, got " + value.getClass().getName(), value instanceof Double); + serialized = solrSerializer.serialize(PROP_DATETIME, value); + deserialized = solrDeserializer.deserialize(PROP_DATETIME, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_FLOAT); + assertTrue("Expected Float object, got " + value.getClass().getName(), value instanceof Float); + serialized = solrSerializer.serialize(PROP_FLOAT, value); + deserialized = solrDeserializer.deserialize(PROP_FLOAT, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_LONG); + assertTrue("Expected Long object, got " + value.getClass().getName(), value instanceof Long); + serialized = solrSerializer.serialize(PROP_LONG, value); + deserialized = solrDeserializer.deserialize(PROP_LONG, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_CHILDASSOC); + serialized = solrSerializer.serialize(PROP_CHILDASSOC, value); + deserialized = solrDeserializer.deserialize(PROP_CHILDASSOC, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_ASSOC); + serialized = solrSerializer.serialize(PROP_ASSOC, value); + deserialized = solrDeserializer.deserialize(PROP_ASSOC, serialized); + assertEquals(value, deserialized); + + value = nodeService.getProperty(solrNode, PROP_CATEGORY); + serialized = solrSerializer.serialize(PROP_ASSOC, value); + deserialized = solrDeserializer.deserialize(PROP_ASSOC, serialized); + assertEquals(value, deserialized); + + return null; + } + }); + } + +} diff --git a/source/java/org/alfresco/repo/web/util/auth/Authorization.java b/source/java/org/alfresco/repo/web/util/auth/Authorization.java deleted file mode 100644 index a3f6218b78..0000000000 --- a/source/java/org/alfresco/repo/web/util/auth/Authorization.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU 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.repo.web.util.auth; - -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.util.ParameterCheck; - -/** - * Helper to process username / password pairs passed to the remote tier - * - * Identifies whether username / password is a ticket. - * - * Is ticket, if one of the following is true: - * - * a) Username == "ROLE_TICKET" (in any case) - * b) Username is not specified (i.e. null) - * c) Username is zero length - */ -public class Authorization -{ - public static String TICKET_USERID = PermissionService.ROLE_PREFIX + "TICKET"; - - private String username; - private String password; - private String ticket; - - /** - * Construct - * - * @param authorization - */ - public Authorization(String authorization) - { - ParameterCheck.mandatoryString("authorization", authorization); - String[] parts = authorization.split(":"); - if (parts.length == 1) - { - setUser(null, parts[0]); - } - else if (parts.length == 2) - { - setUser(parts[0], parts[1]); - } - else - { - throw new IllegalArgumentException("authorization does not consist of username and password"); - } - } - - /** - * Construct - * - * @param username - * @param password - */ - public Authorization(String username, String password) - { - setUser(username, password); - } - - private void setUser(String username, String password) - { - this.username = username; - this.password = password; - if (username == null || username.length() == 0 || username.equalsIgnoreCase(TICKET_USERID)) - { - - this.ticket = password; - } - } - - public String getUserName() - { - return username; - } - - public String getPassword() - { - return password; - } - - public boolean isTicket() - { - return ticket != null; - } - - public String getTicket() - { - return ticket; - } - -} diff --git a/source/java/org/alfresco/repo/web/util/auth/AuthorizationTest.java b/source/java/org/alfresco/repo/web/util/auth/AuthorizationTest.java deleted file mode 100644 index 1ffb7d6b45..0000000000 --- a/source/java/org/alfresco/repo/web/util/auth/AuthorizationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU 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.repo.web.util.auth; - -import junit.framework.TestCase; - - -/** - * Test Authorization - */ -public class AuthorizationTest extends TestCase -{ - private static String USER = "user"; - private static String PASSWORD = "pass"; - - public void testInvalidAuthorization() - { - try - { - new Authorization(null); - fail(); - } - catch(IllegalArgumentException e) - { - } - try - { - new Authorization("username:password:invalid"); - fail(); - } - catch(IllegalArgumentException e) - { - } - } - - public void testAuthorization() - { - Authorization auth1 = new Authorization(USER, PASSWORD); - assertUserPass(USER, PASSWORD, auth1); - Authorization auth2 = new Authorization("", PASSWORD); - assertTicket("", PASSWORD, auth2); - Authorization auth3 = new Authorization(null, PASSWORD); - assertTicket(null, PASSWORD, auth3); - Authorization auth4 = new Authorization(Authorization.TICKET_USERID, PASSWORD); - assertTicket(Authorization.TICKET_USERID, PASSWORD, auth4); - Authorization auth5 = new Authorization(Authorization.TICKET_USERID.toLowerCase(), PASSWORD); - assertTicket(Authorization.TICKET_USERID.toLowerCase(), PASSWORD, auth5); - } - - public void testUserPass() - { - Authorization auth1 = new Authorization(USER + ":" + PASSWORD); - assertUserPass(USER, PASSWORD, auth1); - Authorization auth2 = new Authorization(":" + PASSWORD); - assertTicket("", PASSWORD, auth2); - Authorization auth3 = new Authorization(PASSWORD); - assertTicket(null, PASSWORD, auth3); - Authorization auth4 = new Authorization(Authorization.TICKET_USERID + ":" + PASSWORD); - assertTicket(Authorization.TICKET_USERID, PASSWORD, auth4); - Authorization auth5 = new Authorization(Authorization.TICKET_USERID.toLowerCase() + ":" + PASSWORD); - assertTicket(Authorization.TICKET_USERID.toLowerCase(), PASSWORD, auth5); - } - - private void assertUserPass(String user, String pass, Authorization auth) - { - assertEquals(user, auth.getUserName()); - assertEquals(pass, auth.getPassword()); - assertFalse(auth.isTicket()); - assertNull(auth.getTicket()); - } - - private void assertTicket(String user, String pass, Authorization auth) - { - assertEquals(user, auth.getUserName()); - assertEquals(pass, auth.getPassword()); - assertTrue(auth.isTicket()); - assertEquals(pass, auth.getTicket()); - } - -}