diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index 67bb69c61f..0bb24a10c3 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -44,6 +44,9 @@ + + + diff --git a/config/alfresco/bootstrap/webScriptsReadme2.xml b/config/alfresco/bootstrap/webScriptsReadme2.xml new file mode 100644 index 0000000000..de4fcb16bb --- /dev/null +++ b/config/alfresco/bootstrap/webScriptsReadme2.xml @@ -0,0 +1,8 @@ + + + + + contentUrl=classpath:alfresco/bootstrap/webscripts/readme.html|mimetype=text/html|size=|encoding=UTF-8|locale=en_US_ + + + \ No newline at end of file diff --git a/config/alfresco/bootstrap/webscripts/readme.html b/config/alfresco/bootstrap/webscripts/readme.html index 84bacba322..015532dddb 100644 --- a/config/alfresco/bootstrap/webscripts/readme.html +++ b/config/alfresco/bootstrap/webscripts/readme.html @@ -1 +1,23 @@ -

Web Scripts

Documentation on how to develop a Web Script may be found here.

Web Scripts allow you to bind new Alfresco-based functionality to a HTTP method and custom URL. A library of URLs may be built up to provide a complete API accessible via HTTP.  They are ideal for building data access & update APIs and simple UI components such as Portlets.  Development of Web Scripts may be performed within Alfresco.  Knowledge of Java is not required.

For example, you could create the following API for your particular application...

Execute a search

GET http://<host>:<port>/alfresco/service/blog/category?c=Web20

Retrieve meta-data for an item in the repository

GET http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release

Update meta-data for an item in the repository

POST http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release?status=Draft

Delete an item in the repository

DELETE http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release

\ No newline at end of file +

Web Scripts

+

+Documentation on how to develop a Web Script may be found here.

Web Scripts allow you to bind new Alfresco-based functionality to a HTTP method and custom URL. A library of URLs may be built up to provide a complete API accessible via HTTP.  They are ideal for building data access & update APIs and simple UI components such as Portlets.  Development of Web Scripts may be performed within Alfresco.  Knowledge of Java is not required.
+

+

+For example, you could create the following API for your particular application... +

+
Execute a search
+

+GET http://<host>:<port>/alfresco/service/blog/category/{category} +

+
Retrieve meta-data for an item in the repository
+

+GET http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release +

+
Update meta-data for an item in the repository
+

+POST http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release?status=Draft +

+
Delete an item in the repository
+

+DELETE http://<host>:<port>/alfresco/service/blog/2007/03/04/new-release +

\ No newline at end of file diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 85beadddb6..5cca3d25be 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -280,6 +280,42 @@ 50
+ + + + + + + + + + + + + + + org.alfresco.repo.avm.storeLookupSharedCache + + + + + + + + + + + + + + + + org.alfresco.repo.avm.storeLookupTransactionalCache + + + 100 + + diff --git a/config/alfresco/extension/index-tracking-context.xml.sample b/config/alfresco/extension/index-tracking-context.xml.sample index 9b04517bcd..ca543463f1 100644 --- a/config/alfresco/extension/index-tracking-context.xml.sample +++ b/config/alfresco/extension/index-tracking-context.xml.sample @@ -3,8 +3,12 @@ - - + + + + + + @@ -13,7 +17,7 @@ - + @@ -28,7 +32,7 @@ @@ -36,15 +40,12 @@ - - - + + + + + @@ -53,7 +54,7 @@ - + @@ -62,13 +63,21 @@ - - 5 - - - 0 + + 15 * * * * ? - --> + + + + + + + + + diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 42dec34e0f..64df8f28c0 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -148,6 +148,7 @@ patch.AVMGuidPatch.result=AVM GUIDS set. patch.webscripts.description=Adds Web Scripts to Data Dictionary. patch.webscripts2.description=Adds Web Scripts (second set) to Data Dictionary. +patch.webscripts3.description=Update Web Scripts ReadMe. patch.webscriptsExtension.description=Adds Web Scripts Extension to Data Dictionary. patch.AVMLayeredSnapshot.description=Set indirectionVersion on Layered Nodes. diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 57498d449b..9e730ad8fe 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1000,5 +1000,22 @@ classpath:alfresco/dbscripts/upgrade/2.1/${db.script.dialect}/AlfrescoSchemaUpdate-2.1-RemoveWcmSubmittedAspect.sql + + + patch.webscripts3 + patch.webscripts3.description + 0 + 104 + 105 + + + + + + / + alfresco/bootstrap/webScriptsReadme2.xml + + + diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml index af5531c19b..a5e04b315e 100644 --- a/config/alfresco/script-services-context.xml +++ b/config/alfresco/script-services-context.xml @@ -129,6 +129,9 @@ + + + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index f88bbe4712..6b17d748b2 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=104 +version.schema=105 diff --git a/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java b/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java index fe812c057b..fbfcb874c0 100644 --- a/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java +++ b/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java @@ -318,7 +318,7 @@ public class JCRDocXMLHandler implements ImportContentHandler { // ensure context matches parse ElementContext context = (ElementContext)contextStack.pop(); - QName elementName = QName.createQName(qName, importResolver); + QName elementName = decodeQName(QName.createQName(qName, importResolver)); if (!context.getElementName().equals(elementName)) { throw new InvalidSerializedDataException("Expected element " + context.getElementName() + " but was " + elementName); diff --git a/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java b/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java index 38be9eef84..59d0ae7aa5 100644 --- a/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java +++ b/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java @@ -280,7 +280,9 @@ public class AttributeServiceTest extends TestCase assertEquals(27, fService.getKeys("map/submap").size()); fService.removeAttribute("map/submap/subsubmap", "b"); assertEquals(25, fService.getKeys("map/submap/subsubmap").size()); + System.out.println("Before-------------------------------------------------------------"); fService.removeAttribute("map/submap", "subsubmap"); + System.out.println("After--------------------------------------------------------------"); assertEquals(26, fService.getKeys("map/submap").size()); fService.removeEntries("map/submap", new AttrAndQuery(new AttrQueryGTE("a"), new AttrQueryLTE("d"))); diff --git a/source/java/org/alfresco/repo/attributes/hibernate/AttributeDAOHibernate.java b/source/java/org/alfresco/repo/attributes/hibernate/AttributeDAOHibernate.java index f9f2f40133..43c8f73bac 100644 --- a/source/java/org/alfresco/repo/attributes/hibernate/AttributeDAOHibernate.java +++ b/source/java/org/alfresco/repo/attributes/hibernate/AttributeDAOHibernate.java @@ -34,6 +34,7 @@ import org.alfresco.repo.attributes.AttrQueryHelperImpl; import org.alfresco.repo.attributes.Attribute; import org.alfresco.repo.attributes.AttributeDAO; import org.alfresco.repo.attributes.ListAttribute; +import org.alfresco.repo.attributes.ListEntry; import org.alfresco.repo.attributes.ListEntryDAO; import org.alfresco.repo.attributes.MapAttribute; import org.alfresco.repo.attributes.MapEntry; @@ -78,24 +79,23 @@ public class AttributeDAOHibernate extends HibernateDaoSupport implements if (attr.getType() == Type.MAP) { MapAttribute map = (MapAttribute)attr; - Collection attrs = map.values(); - fMapEntryDAO.delete(map); - for (Attribute subAttr : attrs) + List mapEntries = fMapEntryDAO.get(map); + for (MapEntry entry : mapEntries) { + Attribute subAttr = entry.getAttribute(); + fMapEntryDAO.delete(entry); delete(subAttr); } } if (attr.getType() == Type.LIST) { - List children = new ArrayList(); - for (Attribute child : attr) + ListAttribute list = (ListAttribute)attr; + List listEntries = fListEntryDAO.get(list); + for (ListEntry entry : listEntries) { - children.add(child); - } - fListEntryDAO.delete((ListAttribute)attr); - for (Attribute child : children) - { - delete(child); + Attribute subAttr = entry.getAttribute(); + fListEntryDAO.delete(entry); + delete(subAttr); } } getSession().delete(attr); diff --git a/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java b/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java index 4a01c3cb42..2856f6318d 100644 --- a/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java +++ b/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java @@ -882,4 +882,21 @@ public class AVMLockingAwareService implements AVMService, ApplicationContextAwa } } } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createDirectory(java.lang.String, java.lang.String, java.util.List, java.util.Map) + */ + public void createDirectory(String path, String name, List aspects, Map properties) + { + fService.createDirectory(path, name, aspects, properties); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createFile(java.lang.String, java.lang.String, java.io.InputStream, java.util.List, java.util.Map) + */ + public void createFile(String path, String name, InputStream in, List aspects, Map properties) + { + grabLock(path + '/' + name); + fService.createFile(path, name, in, aspects, properties); + } } diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 314baba190..a4653989d5 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -254,7 +254,7 @@ public class AVMRepository * @param name The name to give the file. * @param data The file contents. */ - public void createFile(String path, String name, File data) + public void createFile(String path, String name, File data, List aspects, Map properties) { fLookupCount.set(1); try @@ -266,7 +266,7 @@ public class AVMRepository throw new AVMNotFoundException("Store not found."); } fLookupCache.onWrite(pathParts[0]); - store.createFile(pathParts[1], name, data); + store.createFile(pathParts[1], name, data, aspects, properties); } finally { @@ -279,7 +279,7 @@ public class AVMRepository * @param path The path to the containing directory. * @param name The name to give the directory. */ - public void createDirectory(String path, String name) + public void createDirectory(String path, String name, List aspects, Map properties) { fLookupCount.set(1); try @@ -291,7 +291,7 @@ public class AVMRepository throw new AVMNotFoundException("Store not found."); } fLookupCache.onWrite(pathParts[0]); - store.createDirectory(pathParts[1], name); + store.createDirectory(pathParts[1], name, aspects, properties); } finally { diff --git a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java index a67ab64d15..7f097a58f6 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java @@ -357,6 +357,14 @@ public class AVMServiceImpl implements AVMService * @param in An InputStream containing data for file. */ public void createFile(String path, String name, InputStream in) + { + createFile(path, name, in, null, null); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createFile(java.lang.String, java.lang.String, java.io.InputStream, java.util.List, java.util.Map) + */ + public void createFile(String path, String name, InputStream in, List aspects, Map properties) { if (path == null || name == null || in == null || !FileNameValidator.IsValid(name)) { @@ -383,7 +391,7 @@ public class AVMServiceImpl implements AVMService } try { - fAVMRepository.createFile(path, name, temp); + fAVMRepository.createFile(path, name, temp, aspects, properties); } finally { @@ -397,12 +405,20 @@ public class AVMServiceImpl implements AVMService * @param name The name of the new directory. */ public void createDirectory(String path, String name) + { + createDirectory(path, name, null, null); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.AVMService#createDirectory(java.lang.String, java.lang.String, java.util.List, java.util.Map) + */ + public void createDirectory(String path, String name, List aspects, Map properties) { if (path == null || name == null || !FileNameValidator.IsValid(name)) { throw new AVMBadArgumentException("Illegal argument."); } - fAVMRepository.createDirectory(path, name); + fAVMRepository.createDirectory(path, name, aspects, properties); } /** diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index 239275f744..58f5413496 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -5043,6 +5043,8 @@ public class AVMServiceTest extends AVMServiceTestBase AVMStoreDescriptor desc = fService.getStore("main"); assertNotNull(desc); System.out.println(desc); + fService.purgeStore("main"); + assertNull(fService.getStore("main")); } catch (Exception e) { diff --git a/source/java/org/alfresco/repo/avm/AVMStore.java b/source/java/org/alfresco/repo/avm/AVMStore.java index 6609c41fb6..9a0996eae0 100644 --- a/source/java/org/alfresco/repo/avm/AVMStore.java +++ b/source/java/org/alfresco/repo/avm/AVMStore.java @@ -87,7 +87,7 @@ public interface AVMStore * @param path The path to the parent directory. * @param name The name to give the new directory. */ - public void createDirectory(String path, String name); + public void createDirectory(String path, String name, List aspects, Map properties); /** * Create a new layered directory. @@ -112,7 +112,7 @@ public interface AVMStore * @param name The name to give the file. * @param data The contents of the file. */ - public void createFile(String path, String name, File data); + public void createFile(String path, String name, File data, List aspects, Map properties); /** * Create a new layered file. diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 5cadfa3ce6..b79d77ff16 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -319,7 +319,7 @@ public class AVMStoreImpl implements AVMStore, Serializable * @param path The path to the containing directory. * @param name The name of the new directory. */ - public void createDirectory(String path, String name) + public void createDirectory(String path, String name, List aspects, Map properties) { Lookup lPath = lookupDirectory(-1, path, true); if (lPath == null) @@ -353,6 +353,14 @@ public class AVMStoreImpl implements AVMStore, Serializable } dir.updateModTime(); dir.putChild(name, newDir); + if (aspects != null) + { + newDir.getAspects().addAll(aspects); + } + if (properties != null) + { + newDir.getProperties().putAll(properties); + } } /** @@ -442,7 +450,7 @@ public class AVMStoreImpl implements AVMStore, Serializable * @param name The name to give the new file. * @param data The contents. */ - public void createFile(String path, String name, File data) + public void createFile(String path, String name, File data, List aspects, Map properties) { Lookup lPath = lookupDirectory(-1, path, true); if (lPath == null) @@ -468,10 +476,19 @@ public class AVMStoreImpl implements AVMStore, Serializable RawServices.Instance().getMimetypeService().guessMimetype(name), -1, "UTF-8")); + if (aspects != null) + { + file.getAspects().addAll(aspects); + } + if (properties != null) + { + file.getProperties().putAll(properties); + } // Yet another flush. AVMDAOs.Instance().fAVMNodeDAO.flush(); ContentWriter writer = createContentWriter(AVMNodeConverter.ExtendAVMPath(path, name)); writer.putContent(data); + } /** diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java b/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java index 7d629268ec..bd3e2d44b3 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java +++ b/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java @@ -24,14 +24,13 @@ package org.alfresco.repo.avm.hibernate; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.alfresco.repo.avm.AVMNode; import org.alfresco.repo.avm.AVMStore; import org.alfresco.repo.avm.AVMStoreDAO; import org.alfresco.repo.avm.AVMStoreImpl; +import org.alfresco.repo.cache.SimpleCache; import org.hibernate.Query; import org.hibernate.proxy.HibernateProxy; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; @@ -46,7 +45,7 @@ class AVMStoreDAOHibernate extends HibernateDaoSupport implements /** * An in memory cache of name to primary key mappings. */ - private Map fNameCache; + private SimpleCache fNameCache; /** * Do nothing constructor. @@ -54,7 +53,11 @@ class AVMStoreDAOHibernate extends HibernateDaoSupport implements public AVMStoreDAOHibernate() { super(); - fNameCache = new HashMap(); + } + + public void setCache(SimpleCache cache) + { + fNameCache = cache; } /** @@ -100,10 +103,7 @@ class AVMStoreDAOHibernate extends HibernateDaoSupport implements public AVMStore getByName(String name) { Long id = null; - synchronized (this) - { - id = fNameCache.get(name); - } + id = fNameCache.get(name); if (id != null) { return forceNonLazy((AVMStore)getSession().get(AVMStoreImpl.class, id)); @@ -112,12 +112,9 @@ class AVMStoreDAOHibernate extends HibernateDaoSupport implements "where st.name = :name"); query.setParameter("name", name); AVMStore result = (AVMStore)query.uniqueResult(); - synchronized (this) + if (result != null) { - if (result != null) - { - fNameCache.put(name, result.getId()); - } + fNameCache.put(name, result.getId()); } return forceNonLazy(result); } diff --git a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java index 6ff43da0c5..ac8a332254 100644 --- a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java +++ b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java @@ -182,7 +182,9 @@ public class AVMLockingServiceTest extends TestCase System.out.println(fAttributeService.getAttribute(".avm_lock_table")); // assertEquals(2, fService.getUsersLocks("Buffy").size()); assertEquals(2, fService.getWebProjectLocks("alfresco").size()); + System.out.println("Before----------------------------"); fService.removeLock("alfresco", "Revello Drive/1630"); + System.out.println("After----------------------------"); System.out.println(fAttributeService.getAttribute(".avm_lock_table")); // assertEquals(1, fService.getUsersLocks("Buffy").size()); assertEquals(1, fService.getWebProjectLocks("alfresco").size()); diff --git a/source/java/org/alfresco/repo/avm/wf/AVMSubmitTransactionListener.java b/source/java/org/alfresco/repo/avm/wf/AVMSubmitTransactionListener.java index a0dcc59081..3d9e074968 100644 --- a/source/java/org/alfresco/repo/avm/wf/AVMSubmitTransactionListener.java +++ b/source/java/org/alfresco/repo/avm/wf/AVMSubmitTransactionListener.java @@ -108,7 +108,9 @@ public class AVMSubmitTransactionListener extends TransactionListenerAdapter true ); if (log.isDebugEnabled()) - log.debug("JMX update to virt server called after commit"); + log.debug("JMX update to virt server called after commit." + + " Version: " + requiresUpdate.getDestinationVersion() + + " Path: " + requiresUpdate.getDestinationPath()); } // Remove virtual webapps from workflow sandbox prior to diff --git a/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java b/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java index 69daa60363..6c5e5e9483 100644 --- a/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java +++ b/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java @@ -99,6 +99,7 @@ public class FSDeploymentTest extends AVMServiceTestBase fService.removeNode("main:/d/e"); fService.createDirectory("main:/d", "e"); fService.createFile("main:/d/e", "Warren.txt").close(); + fService.createFile("main:/d/e", "It's a silly name.txt").close(); report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); count = 0; for (DeploymentEvent event : report) @@ -106,7 +107,7 @@ public class FSDeploymentTest extends AVMServiceTestBase System.out.println(event); count++; } - assertEquals(4, count); + assertEquals(5, count); } catch (Exception e) { diff --git a/source/java/org/alfresco/repo/jscript/AVM.java b/source/java/org/alfresco/repo/jscript/AVM.java index 9344bcdba6..66b3d3e989 100644 --- a/source/java/org/alfresco/repo/jscript/AVM.java +++ b/source/java/org/alfresco/repo/jscript/AVM.java @@ -24,13 +24,21 @@ */ package org.alfresco.repo.jscript; +import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.alfresco.config.JNDIConstants; import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.alfresco.service.cmr.avmsync.AVMDifference; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.NameMatcher; import org.alfresco.util.ParameterCheck; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -44,6 +52,8 @@ public final class AVM extends BaseScopableProcessorExtension { /** Repository Service Registry */ private ServiceRegistry services; + + private NameMatcher matcher; /** * Set the service registry @@ -54,6 +64,11 @@ public final class AVM extends BaseScopableProcessorExtension { this.services = serviceRegistry; } + + public void setNameMatcher(NameMatcher matcher) + { + this.matcher = matcher; + } /** * @return a array of all AVM stores in the system @@ -136,15 +151,201 @@ public final class AVM extends BaseScopableProcessorExtension } return node; } + + /** + * Return the list of modified items for the specified user sandbox against staging store id + * for a specific webapp. + * + * @param storeId Root Store ID + * @param username Username to get modified items for + * @param webapp Webapp name to filter by + * + * @return List of AVMNode objects representing the modified items + */ + public List getModifiedItems(String storeId, String username, String webapp) + { + ParameterCheck.mandatoryString("Store ID", storeId); + ParameterCheck.mandatoryString("Username", username); + ParameterCheck.mandatoryString("Webapp", webapp); + List items; + + AVMService avmService = this.services.getAVMService(); + + // build the paths to the stores to compare - filter by current webapp + String userStore = userSandboxStore(storeId, username); + String userStorePath = getStoreRootWebappPath(userStore, webapp); + String stagingStore = stagingStore(storeId); + String stagingStorePath = getStoreRootWebappPath(stagingStore, webapp); + + List diffs = this.services.getAVMSyncService().compare( + -1, userStorePath, -1, stagingStorePath, this.matcher); + items = new ArrayList(diffs.size()); + for (AVMDifference diff : diffs) + { + // convert each diff record into an AVM Node template wrapper + String sourcePath = diff.getSourcePath(); + AVMNodeDescriptor node = avmService.lookup(-1, sourcePath); + if (node != null) + { + items.add(new AVMNode(node.getPath(), -1, this.services, getScope())); + } + } + + return items; + } + + /** + * @param storeId Store ID to build staging store name for + * + * @return the Staging Store name for the given store ID + */ + public static String stagingStore(String storeId) + { + ParameterCheck.mandatoryString("Store ID", storeId); + return storeId; + } + + /** + * @param storeId Store ID to build sandbox store name for + * @param username Username of the sandbox user + * + * @return the Sandbox Store name for the given store ID and username + */ + public static String userSandboxStore(String storeId, String username) + { + ParameterCheck.mandatoryString("Store ID", storeId); + ParameterCheck.mandatoryString("Username", username); + return storeId + "--" + username; + } + + /** + * @param storeId Store ID to build preview URL for + * + * @return the preview URL to the staging store for the specified store ID + */ + public String websiteStagingUrl(String storeId) + { + ParameterCheck.mandatoryString("Store ID", storeId); + return MessageFormat.format(JNDIConstants.PREVIEW_SANDBOX_URL, + lookupStoreDNS(storeId), getVServerDomain(), getVServerPort()); + } + + /** + * @param storeId Store ID to build preview URL for + * @param username Username to build sandbox preview URL for + * + * @return the preview URL to the user sandbox for the specified store ID and username + */ + public String websiteUserSandboxUrl(String storeId, String username) + { + ParameterCheck.mandatoryString("Store ID", storeId); + ParameterCheck.mandatoryString("Username", username); + return websiteStagingUrl(userSandboxStore(storeId, username)); + } + + /** + * @param store Store ID of the asset + * @param assetPath Store relative path to the asset + * + * @return the preview URL to the specified store asset + */ + public String assetUrl(String store, String assetPath) + { + ParameterCheck.mandatoryString("Store", store); + ParameterCheck.mandatoryString("Asset Path", assetPath); + + if (assetPath.startsWith('/' + JNDIConstants.DIR_DEFAULT_WWW + + '/' + JNDIConstants.DIR_DEFAULT_APPBASE)) + { + assetPath = assetPath.substring(('/' + JNDIConstants.DIR_DEFAULT_WWW + + '/' + JNDIConstants.DIR_DEFAULT_APPBASE).length()); + } + if (assetPath.startsWith("/ROOT")) + { + assetPath = assetPath.substring(("/ROOT").length()); + } + if (assetPath.length() == 0 || assetPath.charAt(0) != '/') + { + assetPath = '/' + assetPath; + } + return MessageFormat.format(JNDIConstants.PREVIEW_ASSET_URL, + lookupStoreDNS(store), getVServerDomain(), getVServerPort(), assetPath); + } + + /** + * @param avmPath Fully qualified AVM path of the asset + * + * @return the preview URL to the specified asset + */ + public String assetUrl(String avmPath) + { + ParameterCheck.mandatoryString("AVM Path", avmPath); + String[] s = avmPath.split(":"); + if (s.length != 2) + { + throw new IllegalArgumentException("Expected exactly one ':' in " + avmPath); + } + return assetUrl(s[0], s[1]); + } + + /** + * @return VServer Port + */ + private String getVServerPort() + { + Integer port = this.services.getVirtServerRegistry().getVirtServerHttpPort(); + if (port == null) + { + port = JNDIConstants.DEFAULT_VSERVER_PORT; + } + return port.toString(); + } + + /** + * @return VServer Domain + */ + private String getVServerDomain() + { + String domain = this.services.getVirtServerRegistry().getVirtServerFQDN(); + if (domain == null) + { + domain = JNDIConstants.DEFAULT_VSERVER_IP; + } + return domain; + } + + /** + * @return the path to the webapps folder in a standard web store. + */ public static String getWebappsFolderPath() { return '/' + JNDIConstants.DIR_DEFAULT_WWW + '/' + JNDIConstants.DIR_DEFAULT_APPBASE; } - + public static String jsGet_webappsFolderPath() { return getWebappsFolderPath(); } + + private static String getStoreRootPath(String store) + { + return store + ":" + getWebappsFolderPath(); + } + + private static String getStoreRootWebappPath(String store, String webapp) + { + return getStoreRootPath(store) + '/' + webapp; + } + + private String lookupStoreDNS(String store) + { + Map props = + this.services.getAVMService().queryStorePropertyKey(store, QName.createQName(null, PROP_DNS + '%')); + return (props.size() == 1 + ? props.keySet().iterator().next().getLocalName().substring(PROP_DNS.length()) : null); + } + + private final static String PROP_DNS = ".dns."; } diff --git a/source/java/org/alfresco/repo/jscript/ContentAwareScriptableQNameMap.java b/source/java/org/alfresco/repo/jscript/ContentAwareScriptableQNameMap.java new file mode 100644 index 0000000000..c7122e59bb --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ContentAwareScriptableQNameMap.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.jscript; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.namespace.QName; + +/** + * Specialised map class for supporting the initialisation of 'cm:content' properties for JavaScript API + * objects. The JavaScript needs supporting objects to be initialised for certain data-types. If the + * 'cm:content' property is not already initialised then it must be created on demand or it will not be + * available to the users of the API. See AR-1673. + * + * @author Kevin Roast + */ +public class ContentAwareScriptableQNameMap extends ScriptableQNameMap +{ + private ServiceRegistry services; + private ScriptNode factory; + + public ContentAwareScriptableQNameMap(ScriptNode factory, ServiceRegistry services) + { + super(services.getNamespaceService()); + this.services = services; + this.factory = factory; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.QNameMap#get(java.lang.Object) + */ + @Override + public Object get(Object name) + { + Object value = super.get(name); + + if (value == null) + { + // convert the key to a qname and look up the data-type for the property + QName qname = QName.resolveToQName(this.resolver, name.toString()); + PropertyDefinition propDef = this.services.getDictionaryService().getProperty(qname); + if (propDef != null && DataTypeDefinition.CONTENT.equals(propDef.getDataType().getName())) + { + // found a valid cm:content property that is not initialised + String mimetype = null; + String fileName = (String)get("cm:name"); + if (fileName != null) + { + mimetype = this.services.getMimetypeService().guessMimetype(fileName); + } + ContentData cdata = new ContentData(null, mimetype, 0L, "UTF-8"); + // create the JavaScript API object we need + value = factory.new ScriptContentData(cdata, ContentModel.PROP_CONTENT); + // and store it so it is available to the API user + put(name, value); + } + } + + return value; + } +} diff --git a/source/java/org/alfresco/repo/jscript/ScriptAction.java b/source/java/org/alfresco/repo/jscript/ScriptAction.java index 8e98be6db6..a3e287b623 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptAction.java +++ b/source/java/org/alfresco/repo/jscript/ScriptAction.java @@ -32,6 +32,7 @@ import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Wrapper; @@ -153,6 +154,31 @@ public final class ScriptAction implements Serializable, Scopeable // Reset the actioned upon node node.reset(); } + + /** + * Execute action + * + * @param nodeRef + * the node to execute action upon + */ + @SuppressWarnings("synthetic-access") + public void execute(NodeRef nodeRef) + { + if (this.parameters != null && this.parameters.isModified()) + { + Map actionParams = action.getParameterValues(); + actionParams.clear(); + + for (Map.Entry entry : this.parameters.entrySet()) + { + // perform the conversion from script wrapper object to repo serializable values + String name = entry.getKey(); + Serializable value = converter.convertActionParamForRepo(name, entry.getValue()); + actionParams.put(name, value); + } + } + services.getActionService().executeAction(action, nodeRef); + } /** * Value converter with specific knowledge of action parameters diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java index b3456b4f6b..b534f1bd7f 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptNode.java +++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java @@ -534,7 +534,9 @@ public class ScriptNode implements Serializable, Scopeable if (this.properties == null) { // this Map implements the Scriptable interface for native JS syntax property access - this.properties = new ScriptableQNameMap(this.services.getNamespaceService()); + // this impl of the QNameMap is capable of creating ScriptContentData on demand for 'cm:content' + // properties that have not been initialised - see AR-1673. + this.properties = new ContentAwareScriptableQNameMap(this, this.services); Map props = this.nodeService.getProperties(this.nodeRef); for (QName qname : props.keySet()) @@ -542,7 +544,6 @@ public class ScriptNode implements Serializable, Scopeable Serializable propValue = props.get(qname); // perform the conversion to a script safe value and store - this.properties.put(qname.toString(), getValueConverter().convertValueForScript(qname, propValue)); } } @@ -774,7 +775,7 @@ public class ScriptNode implements Serializable, Scopeable { String content = ""; - ScriptContentData contentData = (ScriptContentData) getProperties().get(ContentModel.PROP_CONTENT); + ScriptContentData contentData = (ScriptContentData)getProperties().get(ContentModel.PROP_CONTENT); if (contentData != null) { content = contentData.getContent(); @@ -795,16 +796,11 @@ public class ScriptNode implements Serializable, Scopeable */ public void setContent(String content) { - ScriptContentData contentData = (ScriptContentData) getProperties().get(ContentModel.PROP_CONTENT); - if (contentData == null) + ScriptContentData contentData = (ScriptContentData)getProperties().get(ContentModel.PROP_CONTENT); + if (contentData != null) { - // guess a mimetype based on the filename - String mimetype = this.services.getMimetypeService().guessMimetype(getName()); - ContentData cdata = new ContentData(null, mimetype, 0L, "UTF-8"); - contentData = new ScriptContentData(cdata, ContentModel.PROP_CONTENT); - getProperties().put(ContentModel.PROP_CONTENT.toString(), contentData); + contentData.setContent(content); } - contentData.setContent(content); } public void jsSet_content(String content) diff --git a/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java index 7b8e02ee5a..e66a2c8e7b 100644 --- a/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java +++ b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java @@ -35,6 +35,9 @@ import org.apache.commons.logging.LogFactory; /** * Track and update when snapshots are created and indexed in a cluster + * + * @author Andy Hind + * @since 2.1.0 */ public class AVMRemoteSnapshotTracker extends AbstractReindexComponent { diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java index b2ae5e745e..e412ccf16e 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java @@ -589,10 +589,7 @@ public abstract class AlfrescoTransactionSupport } // These are still considered part of the transaction so are executed here - for (TransactionListener listener : getListenersIterable()) - { - listener.beforeCommit(readOnly); - } + doBeforeCommit(readOnly); // Check integrity for (IntegrityChecker integrityChecker : integrityCheckers) @@ -607,6 +604,39 @@ public abstract class AlfrescoTransactionSupport } } + /** + * Execute the beforeCommit event handlers for the registered listeners + * + * @param readOnly is read only + */ + private void doBeforeCommit(boolean readOnly) + { + doBeforeCommit(new HashSet(listeners.size()), readOnly); + } + + /** + * Executes the beforeCommit event handlers for the outstanding listeners + * + * @param visitedListeners a set containing the already visited listeners + * @param readOnly is read only + */ + private void doBeforeCommit(Set visitedListeners, boolean readOnly) + { + Set pendingListeners = new HashSet(listeners); + pendingListeners.removeAll(visitedListeners); + + if (pendingListeners.size() != 0) + { + for (TransactionListener listener : pendingListeners) + { + listener.beforeCommit(readOnly); + visitedListeners.add(listener); + } + + doBeforeCommit(visitedListeners, readOnly); + } + } + @Override public void beforeCompletion() { diff --git a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java index 7d92be6f87..1c402826b1 100644 --- a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java +++ b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java @@ -29,6 +29,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import org.alfresco.model.ContentModel; @@ -42,6 +43,7 @@ import org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicy; import org.alfresco.service.cmr.repository.ContentData; 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.StoreRef; @@ -107,6 +109,9 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest protected static final String MULTI_VALUE_1 = "multi1"; protected static final String MULTI_VALUE_2 = "multi2"; + protected MLText mlText; + protected static final QName MLTEXT_PROP = QName.createQName(TEST_NAMESPACE, "propMl"); + /** * Test content */ @@ -162,7 +167,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest this.versionProperties = new HashMap(); versionProperties.put(VERSION_PROP_1, VALUE_1); versionProperties.put(VERSION_PROP_2, VALUE_2); - versionProperties.put(VERSION_PROP_3, VALUE_3); + versionProperties.put(VERSION_PROP_3, VALUE_3); // Create the node properties this.nodeProperties = new HashMap(); @@ -172,6 +177,11 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest this.nodeProperties.put(MULTI_PROP, (Serializable)multiValue); this.nodeProperties.put(ContentModel.PROP_CONTENT, new ContentData(null, "text/plain", 0L, "UTF-8")); + // Add mlText property + this.mlText = new MLText(Locale.UK, "UK value"); + this.mlText.addValue(Locale.US, "Y'all US value"); + this.nodeProperties.put(MLTEXT_PROP, this.mlText); + // Create a workspace that contains the 'live' nodes this.testStoreRef = this.dbNodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); diff --git a/source/java/org/alfresco/repo/version/NodeServiceImplTest.java b/source/java/org/alfresco/repo/version/NodeServiceImplTest.java index 3341223ebb..3acd5fb676 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImplTest.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImplTest.java @@ -146,6 +146,9 @@ public class NodeServiceImplTest extends BaseVersionStoreTest PROP_1); assertEquals(VALUE_1, value1); + // Check the mlText property + // TODO + // Check the multi values property specifically Collection multiValue = (Collection)this.lightWeightVersionStoreNodeService.getProperty(version.getFrozenStateNodeRef(), MULTI_PROP); assertNotNull(multiValue); diff --git a/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml b/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml index 2d707a64bc..f8d350d311 100644 --- a/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml +++ b/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml @@ -37,6 +37,11 @@ false + + d:mltext + false + + d:text true diff --git a/source/java/org/alfresco/repo/version/common/AbstractVersionServiceImpl.java b/source/java/org/alfresco/repo/version/common/AbstractVersionServiceImpl.java index 2fc073aa71..55e7a7776a 100644 --- a/source/java/org/alfresco/repo/version/common/AbstractVersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/common/AbstractVersionServiceImpl.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.repo.node.MLPropertyInterceptor; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyScope; @@ -231,13 +232,21 @@ public abstract class AbstractVersionServiceImpl ClassDefinition classDefinition = this.dictionaryService.getClass(classRef); if (classDefinition != null) { - // Copy the properties - Map propertyDefinitions = classDefinition.getProperties(); - for (QName propertyName : propertyDefinitions.keySet()) + boolean wasMLAware = MLPropertyInterceptor.setMLAware(true); + try { - Serializable propValue = this.nodeService.getProperty(nodeRef, propertyName); - nodeDetails.addProperty(classRef, propertyName, propValue); - } + // Copy the properties + Map propertyDefinitions = classDefinition.getProperties(); + for (QName propertyName : propertyDefinitions.keySet()) + { + Serializable propValue = this.nodeService.getProperty(nodeRef, propertyName); + nodeDetails.addProperty(classRef, propertyName, propValue); + } + } + finally + { + MLPropertyInterceptor.setMLAware(wasMLAware); + } // Version the associations (child and target) Map assocDefs = classDefinition.getAssociations(); diff --git a/source/java/org/alfresco/service/cmr/avm/AVMService.java b/source/java/org/alfresco/service/cmr/avm/AVMService.java index 7046bd07b4..744a2fb9ed 100644 --- a/source/java/org/alfresco/service/cmr/avm/AVMService.java +++ b/source/java/org/alfresco/service/cmr/avm/AVMService.java @@ -324,8 +324,6 @@ public interface AVMService */ public OutputStream createFile(String path, String name); - - /** * Create a new "plain" (non-layered) file. * Guarantees that the entire contents of the @@ -341,7 +339,24 @@ public interface AVMService */ public void createFile(String path, String name, InputStream in); - + + /** + * Create a new "plain" (non-layered) file. + * Guarantees that the entire contents of the + * input stream will be loaded atomically. + * The directory identified by path must already exist. + * + * @param path The path of the directory containing the created file. + * @param name The name of the new file + * @param in An input stream with data for the file. + * @param aspect A list of aspects to give the file. + * @param properties A map of properties to give the file. + * @throws AVMNotFound + * @throws AVMExists + * @throws AVMWrongType + */ + public void createFile(String path, String name, InputStream in, List aspects, Map properties); + /** * Create a new directory. * If path is within a layer, the new directory will be a layered directory; @@ -355,6 +370,20 @@ public interface AVMService */ public void createDirectory(String path, String name); + /** + * Create a new directory. + * If path is within a layer, the new directory will be a layered directory; + * otherwise, the new directory will be a plain directory. + * + * @param path The simple absolute path to the parent. + * @param name The name to give the directory. + * @param aspects A list of aspects to add. + * @param properties A Map of properties to add. + * @throws AVMNotFound + * @throws AVMExists + * @throws AVMWrongType + */ + public void createDirectory(String path, String name, List aspects, Map properties); /** * Create a new layered file.