diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties
index acbd9d1c2f..46bb07ce49 100644
--- a/config/alfresco/messages/patch-service.properties
+++ b/config/alfresco/messages/patch-service.properties
@@ -16,4 +16,5 @@ patch.updatePermissionData.result=Created the following permission reference nam
patch.guestUser.description=Add the guest user, guest home space; and fix permissions on company home, guest home and guest person.
patch.guestUser.result=Added guest user and fixed permissions.
-
+patch.fixNodeSerializableValues.description=Ensure that property values are not stored as Serializable if at all possible
+patch.fixNodeSerializableValues.result=Fixed {0} node property serialized values
diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml
index 8e739cf6ed..ba3b4c2eca 100644
--- a/config/alfresco/patch/patch-services-context.xml
+++ b/config/alfresco/patch/patch-services-context.xml
@@ -106,7 +106,6 @@
-
+ * A guest user homespace is now created during bootstrap. It is required for guest user
+ * access, but in older databases will not exist.
+ *
+ * @author Andy Hind
+ */
public class GuestUserPatch extends AbstractPatch
{
private static final String MSG_SUCCESS = "patch.guestUser.result";
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java
new file mode 100644
index 0000000000..828346c2d0
--- /dev/null
+++ b/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.admin.patch.impl;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.alfresco.i18n.I18NUtil;
+import org.alfresco.repo.admin.patch.AbstractPatch;
+import org.alfresco.repo.domain.Node;
+import org.alfresco.repo.domain.PropertyValue;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.namespace.QName;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.springframework.orm.hibernate3.HibernateCallback;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
+
+/**
+ * Certain content models make extensive use of the d:any datatype, which has led
+ * to storage of simple types as serialized instances.
+ * This patch ensures that all previously serializable values are stored in their
+ * more native form in the database.
+ * e.g. If a property was d:any and a string was written ("ABC"),
+ * then the value was stored in serializable_value. Instead, the newer code stores
+ * the value in string_value. None of the retrieval code is affected, but the values
+ * are made visible to queries, in addition to reducing the size of the node_properties
+ * table. This patch ensures that previously-stored values are changed to conform
+ * to the new storage mechanism.
+ *
+ * JIRA: {@link http://www.alfresco.org/jira/browse/AR-359 AR-359}
+ *
+ * @see org.alfresco.repo.domain.PropertyValue
+ *
+ * @author Derek Hulley
+ */
+public class NodePropertySerializablePatch extends AbstractPatch
+{
+ private static final String MSG_SUCCESS = "patch.fixNodeSerializableValues.result";
+
+ private HibernateHelper helper;
+
+ public NodePropertySerializablePatch()
+ {
+ helper = new HibernateHelper();
+ }
+
+ public void setSessionFactory(SessionFactory sessionFactory)
+ {
+ this.helper.setSessionFactory(sessionFactory);
+ }
+
+ @Override
+ protected String applyInternal() throws Exception
+ {
+ int updatedEntries = helper.fixSerializableProperties();
+
+ // build the result message
+ String msg = I18NUtil.getMessage(MSG_SUCCESS, updatedEntries);
+ // done
+ return msg;
+ }
+
+ private static class HibernateHelper extends HibernateDaoSupport
+ {
+ private static final String QUERY_GET_NODES = "node.patch.GetNodesWithPersistedSerializableProperties";
+
+ public int fixSerializableProperties()
+ {
+ HibernateCallback callback = new HibernateCallback()
+ {
+ @SuppressWarnings("unchecked")
+ public Object doInHibernate(Session session)
+ {
+ Query query = session.getNamedQuery(HibernateHelper.QUERY_GET_NODES);
+ IteratorSerializable
persistence is the last resort, being both
+ * more verbose as well as unusable in queries, the value is used to
+ * determine a more appropriate persistent type. This method defers to
+ * the other, more basic types' version of this method to fulfill the request.
+ */
+ @Override
+ protected ValueType getPersistedType(Serializable value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Persisted type cannot be determined by null value");
+ }
+ // check the value to determine the most appropriate type to defer to
+ if (value instanceof Boolean)
+ {
+ return ValueType.BOOLEAN.getPersistedType(value);
+ }
+ else if ((value instanceof Integer) || (value instanceof Long))
+ {
+ return ValueType.LONG.getPersistedType(value);
+ }
+ else if (value instanceof Float)
+ {
+ return ValueType.FLOAT.getPersistedType(value);
+ }
+ else if (value instanceof Double)
+ {
+ return ValueType.DOUBLE.getPersistedType(value);
+ }
+ else if (value instanceof String)
+ {
+ return ValueType.STRING.getPersistedType(value);
+ }
+ else if (value instanceof Date)
+ {
+ return ValueType.DATE.getPersistedType(value);
+ }
+ else if (value instanceof ContentData)
+ {
+ return ValueType.CONTENT.getPersistedType(value);
+ }
+ else if (value instanceof NodeRef)
+ {
+ return ValueType.NODEREF.getPersistedType(value);
+ }
+ else if (value instanceof QName)
+ {
+ return ValueType.QNAME.getPersistedType(value);
+ }
+ else if (value instanceof Path)
+ {
+ return ValueType.PATH.getPersistedType(value);
+ }
+ else
+ {
+ return this;
+ }
+ }
+
@Override
Serializable convert(Serializable value)
{
@@ -137,7 +214,7 @@ public class PropertyValue implements Cloneable, Serializable
CONTENT
{
@Override
- protected ValueType getPersistedType()
+ protected ValueType getPersistedType(Serializable value)
{
return ValueType.STRING;
}
@@ -151,7 +228,7 @@ public class PropertyValue implements Cloneable, Serializable
NODEREF
{
@Override
- protected ValueType getPersistedType()
+ protected ValueType getPersistedType(Serializable value)
{
return ValueType.STRING;
}
@@ -165,7 +242,7 @@ public class PropertyValue implements Cloneable, Serializable
QNAME
{
@Override
- protected ValueType getPersistedType()
+ protected ValueType getPersistedType(Serializable value)
{
return ValueType.STRING;
}
@@ -179,7 +256,7 @@ public class PropertyValue implements Cloneable, Serializable
PATH
{
@Override
- protected ValueType getPersistedType()
+ protected ValueType getPersistedType(Serializable value)
{
return ValueType.SERIALIZABLE;
}
@@ -191,8 +268,12 @@ public class PropertyValue implements Cloneable, Serializable
}
};
- /** override if the type gets persisted in a different format */
- protected ValueType getPersistedType()
+ /**
+ * Override if the type gets persisted in a different format
+ *
+ * @param value the actual value that is to be persisted. May not be null.
+ */
+ protected ValueType getPersistedType(Serializable value)
{
return this;
}
@@ -302,7 +383,7 @@ public class PropertyValue implements Cloneable, Serializable
{
// get the persisted type
ValueType valueType = makeValueType(typeQName);
- ValueType persistedValueType = valueType.getPersistedType();
+ ValueType persistedValueType = valueType.getPersistedType(value);
// convert to the persistent type
value = persistedValueType.convert(value);
setPersistedValue(persistedValueType, value);
diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
index 0a43cd27da..452d5f6051 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
+++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
@@ -330,4 +330,14 @@
status.changeTxnId = :changeTxnId
+