/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.core;
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.AccessControlList;
import org.exoplatform.services.jcr.access.AccessControlPolicy;
import org.exoplatform.services.jcr.config.RepositoryEntry;
import org.exoplatform.services.jcr.core.ComponentPersister;
import org.exoplatform.services.jcr.core.ExtendedPropertyType;
import org.exoplatform.services.jcr.dataflow.DataManager;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.dataflow.TransientItemData;
import org.exoplatform.services.jcr.impl.dataflow.TransientNodeData;
import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
import org.exoplatform.services.jcr.impl.dataflow.TransientValueData;
import org.exoplatform.services.jcr.impl.dataflow.ValueDataUtil;
import org.exoplatform.services.jcr.impl.util.NodeDataReader;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
/**
* Created by The eXo Platform SAS.
*
* @author <a href="mailto:gennady.azarenkov@exoplatform.com">Gennady
* Azarenkov</a>
* @version $Id: NamespaceDataPersister.java 13962 2008-05-07 16:00:48Z
* pnedonosko $
*/
public class NamespaceDataPersister implements ComponentPersister
{
public static Log log = ExoLogger.getLogger("exo.jcr.component.core.NamespaceDataPersister");
private final DataManager dataManager;
private NodeData nsRoot;
private final RepositoryEntry repConfig;
private boolean started = false;
public NamespaceDataPersister(DataManager dataManager, RepositoryEntry repConfig) throws RepositoryException
{
this.dataManager = dataManager;
this.repConfig = repConfig;
}
/**
* Add new namespace.
*
* @param prefix
* NS prefix
* @param uri
* NS URI
* @throws RepositoryException
* Repository error
*/
public void addNamespace(String prefix, String uri) throws RepositoryException
{
if (!started)
{
if (log.isDebugEnabled())
log.debug("Unable save namespace " + uri + "=" + prefix + " in to the storage. Storage not initialized");
return;
}
PlainChangesLog changesLog = new PlainChangesLogImpl();
internallAdd(changesLog, prefix, uri);
dataManager.save(new TransactionChangesLog(changesLog));
}
private PlainChangesLog internallAdd(PlainChangesLog changesLog, String prefix, String uri)
{
TransientNodeData nsNode =
TransientNodeData.createNodeData(nsRoot, new InternalQName("", prefix), Constants.EXO_NAMESPACE);
TransientPropertyData primaryType =
TransientPropertyData.createPropertyData(nsNode, Constants.JCR_PRIMARYTYPE, PropertyType.NAME, false,
new TransientValueData(nsNode.getPrimaryTypeName()));
TransientPropertyData exoUri =
TransientPropertyData.createPropertyData(nsNode, Constants.EXO_URI_NAME, PropertyType.STRING, false,
new TransientValueData(uri));
TransientPropertyData exoPrefix =
TransientPropertyData.createPropertyData(nsNode, Constants.EXO_PREFIX, PropertyType.STRING, false,
new TransientValueData(prefix));
changesLog.add(ItemState.createAddedState(nsNode)).add(ItemState.createAddedState(primaryType)).add(
ItemState.createAddedState(exoUri)).add(ItemState.createAddedState(exoPrefix));
return changesLog;
}
/**
* Add new namespace.
*
* @param namespaceMap
* @throws RepositoryException Repository error
*/
public void addNamespaces(Map<String, String> namespaceMap) throws RepositoryException
{
if (!started)
{
log.warn("Unable save namespaces in to the storage. Storage not initialized");
return;
}
PlainChangesLog changesLog = new PlainChangesLogImpl();
for (Map.Entry<String, String> entry : namespaceMap.entrySet())
{
String prefix = entry.getKey();
String uri = entry.getValue();
if (prefix != null)
{
if (log.isDebugEnabled())
log.debug("Namespace " + uri + ":" + prefix);
internallAdd(changesLog, prefix, uri);
}
}
dataManager.save(new TransactionChangesLog(changesLog));
}
public boolean isStorageFilled()
{
try
{
List<NodeData> storageContent = dataManager.getChildNodesData(nsRoot);
return storageContent.size() > 0;
}
catch (RepositoryException e)
{
log.error(e.getLocalizedMessage(), e);
}
return false;
}
public void removeNamespace(String prefix) throws RepositoryException
{
if (!started)
{
log.warn("Unable remove namspace " + prefix + " from the storage. Storage not initialized");
return;
}
PlainChangesLogImpl plainChangesLogImpl = new PlainChangesLogImpl();
ItemData prefData =
dataManager.getItemData(nsRoot, new QPathEntry(new InternalQName("", prefix), 0), ItemType.NODE);
if (prefData != null && prefData.isNode())
{
List<PropertyData> childs = dataManager.getChildPropertiesData((NodeData)prefData);
for (PropertyData propertyData : childs)
{
plainChangesLogImpl.add(ItemState.createDeletedState(copyPropertyData(propertyData), true));
}
prefData =
new TransientNodeData(prefData.getQPath(), prefData.getIdentifier(), prefData.getPersistedVersion(),
((NodeData)prefData).getPrimaryTypeName(), ((NodeData)prefData).getMixinTypeNames(),
((NodeData)prefData).getOrderNumber(), ((NodeData)prefData).getParentIdentifier(), ((NodeData)prefData)
.getACL());
plainChangesLogImpl.add(ItemState.createDeletedState(prefData, true));
}
dataManager.save(new TransactionChangesLog(plainChangesLogImpl));
}
/**
* {@inheritDoc}
*/
public void start()
{
if (!started)
{
try
{
NodeData jcrSystem = (NodeData)dataManager.getItemData(Constants.SYSTEM_UUID);
if (jcrSystem != null)
{
NodeData exoNamespaces =
(NodeData)dataManager.getItemData(jcrSystem, new QPathEntry(Constants.EXO_NAMESPACES, 1),
ItemType.NODE);
if (exoNamespaces == null)
{
initStorage(jcrSystem, !repConfig.getAccessControl().equals(AccessControlPolicy.DISABLE));
this.nsRoot =
(NodeData)dataManager.getItemData(jcrSystem, new QPathEntry(Constants.EXO_NAMESPACES, 1),
ItemType.NODE);
}
else
{
this.nsRoot = exoNamespaces;
}
}
else
{
throw new RepositoryException("Nodetypes storage (/jcr:systemnode) is not initialized.");
}
}
catch (RepositoryException e)
{
throw new RuntimeException(e.getLocalizedMessage(), e);
}
started = true;
}
}
public void stop()
{
}
void loadNamespaces(Map<String, String> namespacesMap, Map<String, String> urisMap) throws RepositoryException
{
if (!isInialized())
{
NodeData jcrSystem = (NodeData)dataManager.getItemData(Constants.SYSTEM_UUID);
if (jcrSystem != null)
this.nsRoot =
(NodeData)dataManager.getItemData(jcrSystem, new QPathEntry(Constants.EXO_NAMESPACES, 1), ItemType.NODE);
else
throw new RepositoryException(
"/jcr:system is not found. Possible the workspace is not initialized properly");
}
if (isInialized())
{
NodeDataReader nsReader = new NodeDataReader(nsRoot, dataManager);
nsReader.setRememberSkiped(true);
nsReader.forNodesByType(Constants.EXO_NAMESPACE);
nsReader.read();
List<NodeDataReader> nsData = nsReader.getNodesByType(Constants.EXO_NAMESPACE);
for (NodeDataReader nsr : nsData)
{
nsr.forProperty(Constants.EXO_URI_NAME, PropertyType.STRING).forProperty(Constants.EXO_PREFIX,
PropertyType.STRING);
nsr.read();
String exoUri = ValueDataUtil.getString(nsr.getPropertyValue(Constants.EXO_URI_NAME));
String exoPrefix = ValueDataUtil.getString(nsr.getPropertyValue(Constants.EXO_PREFIX));
namespacesMap.put(exoPrefix, exoUri);
urisMap.put(exoUri, exoPrefix);
if (log.isDebugEnabled())
log.debug("Namespace " + exoPrefix + " is loaded");
}
for (NodeData skipedNs : nsReader.getSkiped())
{
log.warn("Namespace node " + skipedNs.getQPath().getName().getAsString() + " (primary type '"
+ skipedNs.getPrimaryTypeName().getAsString()
+ "') is not supported for loading. Nodes with 'exo:namespace' node type is supported only now.");
}
}
else
log.warn("Namespace storage (/jcr:system/exo:namespaces node) is not initialized. No namespaces loaded.");
}
/**
* Copy <code>PropertyData prop</code> to new TransientItemData
*
* @param prop
* @return
* @throws RepositoryException
*/
private TransientItemData copyPropertyData(PropertyData prop) throws RepositoryException
{
if (prop == null)
return null;
// make a copy, value may be null for deleting items
TransientPropertyData newData =
new TransientPropertyData(prop.getQPath(), prop.getIdentifier(), prop.getPersistedVersion(), prop.getType(),
prop.getParentIdentifier(), prop.isMultiValued(), prop.getValues());
return newData;
}
/**
* Creates namespaces storage and fill it with given namespaces.
*
* @param nsSystem
* @param addACL
* @param namespaces
* @throws RepositoryException
*/
private void initStorage(NodeData nsSystem, boolean addACL) throws RepositoryException
{
PlainChangesLog changesLog = new PlainChangesLogImpl();
TransientNodeData exoNamespaces;
if (addACL)
{
InternalQName[] mixins = new InternalQName[]{Constants.EXO_OWNEABLE, Constants.EXO_PRIVILEGEABLE};
exoNamespaces =
TransientNodeData.createNodeData(nsSystem, Constants.EXO_NAMESPACES, Constants.NT_UNSTRUCTURED, mixins);
AccessControlList acl = exoNamespaces.getACL();
TransientPropertyData primaryType =
TransientPropertyData.createPropertyData(exoNamespaces, Constants.JCR_PRIMARYTYPE, PropertyType.NAME,
false, new TransientValueData(exoNamespaces.getPrimaryTypeName()));
changesLog.add(ItemState.createAddedState(exoNamespaces)).add(ItemState.createAddedState(primaryType));
// jcr:mixinTypes
List<ValueData> mixValues = new ArrayList<ValueData>();
for (InternalQName mixin : mixins)
{
mixValues.add(new TransientValueData(mixin));
}
TransientPropertyData exoMixinTypes =
TransientPropertyData.createPropertyData(exoNamespaces, Constants.JCR_MIXINTYPES, PropertyType.NAME, true,
mixValues);
TransientPropertyData exoOwner =
TransientPropertyData.createPropertyData(exoNamespaces, Constants.EXO_OWNER, PropertyType.STRING, false,
new TransientValueData(acl.getOwner()));
List<ValueData> permsValues = new ArrayList<ValueData>();
for (int i = 0; i < acl.getPermissionEntries().size(); i++)
{
AccessControlEntry entry = acl.getPermissionEntries().get(i);
permsValues.add(new TransientValueData(entry));
}
TransientPropertyData exoPerms =
TransientPropertyData.createPropertyData(exoNamespaces, Constants.EXO_PERMISSIONS,
ExtendedPropertyType.PERMISSION, true, permsValues);
changesLog.add(ItemState.createAddedState(exoMixinTypes)).add(ItemState.createAddedState(exoOwner)).add(
ItemState.createAddedState(exoPerms));
changesLog.add(new ItemState(exoNamespaces, ItemState.MIXIN_CHANGED, false, null));
}
else
{
exoNamespaces =
TransientNodeData.createNodeData(nsSystem, Constants.EXO_NAMESPACES, Constants.NT_UNSTRUCTURED);
TransientPropertyData primaryType =
TransientPropertyData.createPropertyData(exoNamespaces, Constants.JCR_PRIMARYTYPE, PropertyType.NAME,
false, new TransientValueData(exoNamespaces.getPrimaryTypeName()));
changesLog.add(ItemState.createAddedState(exoNamespaces)).add(ItemState.createAddedState(primaryType));
}
nsRoot = exoNamespaces;
dataManager.save(new TransactionChangesLog(changesLog));
}
private boolean isInialized()
{
return nsRoot != null;
}
}