/*
* Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.arsdigita.cms;
import com.arsdigita.cms.publishToFile.QueueManager;
import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.domain.DomainObject;
import com.arsdigita.domain.DomainObjectFactory;
import com.arsdigita.domain.DomainServiceInterfaceExposer;
import com.arsdigita.kernel.ACSObject;
import com.arsdigita.persistence.DataAssociation;
import com.arsdigita.persistence.DataAssociationCursor;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.Session;
import com.arsdigita.persistence.SessionManager;
import com.arsdigita.persistence.metadata.ObjectType;
import com.arsdigita.persistence.metadata.Property;
import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import org.apache.log4j.Logger;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This class represents an association between a pending or live ContentItem
* (or one of its components) and a separate top-level Contenttem.
*
*/
class PublishedLink extends DomainObject {
private static final Logger s_log = Logger.getLogger(PublishedLink.class);
static final String SOURCE_MASTER_ITEM = "pending";
// replace below later with:
//public static final String PENDING_OID = "pendingOID"
static final String PENDING_SOURCE = "pendingSource";
static final String PROPERTY_NAME = "propertyName";
static final String DRAFT_TARGET = "draftTarget";
static final String LINK_ATTRIBUTES = "linkAttributes";
static final String BASE_DATA_OBJECT_TYPE =
"com.arsdigita.cms.PublishedLink";
@Override
protected String getBaseDataObjectType() {
return BASE_DATA_OBJECT_TYPE;
}
/**
* Default constructor. The contained
* DataObject is initialized with a new
* DataObject with an
* ObjectType of "PublishedLink".
*
* @see com.arsdigita.domain.DomainObject#DomainObject(String)
* @see com.arsdigita.persistence.DataObject
* @see com.arsdigita.persistence.metadata.ObjectType
*/
protected PublishedLink() {
super(BASE_DATA_OBJECT_TYPE);
}
/**
* Creates a PublishedLink object with the specified data object.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(DataObject)
*/
protected PublishedLink(DataObject data) {
super(data);
}
/**
* Constructor. The contained
* DataObject is retrieved from the persistent storage
* mechanism with an
* OID specified by oid.
*
* @param oid The
* OID for the retrieved
* DataObject.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(OID)
* @see com.arsdigita.persistence.DataObject
* @see com.arsdigita.persistence.OID
*/
protected PublishedLink(OID oid) throws DataObjectNotFoundException {
super(oid);
}
/**
* Creates new PublishedLink unless one already exists for the specified
* source, target, and property
*
* @param sourceMasterItem the top-level pending or live
* ContentItem which this
* PublishedLink is a component of.
* @param linkSource the immediate source of this
* PublishedLink, a component of the sourceMasterItem (or the
* item itself)
* @param propertyName the Property name for this
* PublishedLink
* @param linkTarget the top-level draft
* ContentItem which is the target of this
* PublishedLink .
*
* @return the newly-created PublishedLink, or the existing one if one
* already exists for these items.
*/
static PublishedLink create(ContentItem sourceMasterItem,
DomainObject linkSource,
String propertyName,
ContentItem linkTarget,
ContentItem sourceObject) {
OID oid = new OID(BASE_DATA_OBJECT_TYPE);
oid.set(SOURCE_MASTER_ITEM, DomainServiceInterfaceExposer.getDataObject(
sourceMasterItem));
oid.set(PROPERTY_NAME, propertyName);
oid.set(DRAFT_TARGET, DomainServiceInterfaceExposer.getDataObject(
linkTarget));
// this will need to be refactored if we switch to OID link sourcess
if (linkSource instanceof ACSObject) {
oid.set(PENDING_SOURCE, DomainServiceInterfaceExposer.getDataObject(
linkSource));
} else {
Assert.fail("Cannot set PublishedLink source " + linkSource
+ "; it is not an "
+ "ACSObject");
}
PublishedLink link = null;
try {
link = new PublishedLink(oid);
} catch (DataObjectNotFoundException e) {
link = new PublishedLink(SessionManager.getSession().create(oid));
}
if ((sourceObject != null) && sourceObject.getObjectType().getProperty(
propertyName).isCollection()) {
DataCollection coll = (DataCollection) sourceObject.get(propertyName
+ "@link");
while (coll.next()) {
DataObject linkObj = coll.getDataObject();
if (linkTarget.getOID().equals(((DataObject) linkObj.getOID().
get(propertyName)).getOID())) {
link.saveLinkAttributes(linkObj);
coll.close();
}
}
}
return link;
}
/**
* Returns the top-level pending or live
* ContentItem which this
* PublishedLink is a component of.
*
* @return the top-level pending or live
* ContentItem which this
* PublishedLink is a component of.
*
*/
ContentItem getSourceMasterItem() {
final DataObject item = (DataObject) get(SOURCE_MASTER_ITEM);
return item == null ? null
: (ContentItem) DomainObjectFactory.newInstance((DataObject) item);
}
/**
* Returns the immediate source of this
* PublishedLink
*
* @return the immediate source of this
* PublishedLink
*
*/
DomainObject getLinkSource() {
// this will need to be refactored if we switch to OIDs
final DataObject item = (DataObject) get(PENDING_SOURCE);
return item == null ? null
: DomainObjectFactory.newInstance((DataObject) item);
}
/**
* Returns the Property name for this
* PublishedLink
*
* @return the Property name for this
* PublishedLink
*
*/
String getPropertyName() {
return (String) get(PROPERTY_NAME);
}
/**
* Returns the top-level draft
* ContentItem which is the target of this
* PublishedLink .
*
* @return the top-level draft
* ContentItem which is the target of this
* PublishedLink .
*
*/
ContentItem getLinkTarget() {
final DataObject item = (DataObject) get(DRAFT_TARGET);
return item == null ? null
: (ContentItem) DomainObjectFactory.newInstance((DataObject) item);
}
/**
* Updates live associations based on PublishedLinks which either point
* from or to the given
* ContentItem. If both ends of the link are now live, the
* live-live association will be updated. In addition, for links to
* this new live item, the source of the link is refreshed via
* QueueManager.queueRepublish.
*
* @param item The item which was just published
*/
public static void updateLiveLinks(ContentItem item) {
Session session = SessionManager.getSession();
Set itemsToRefresh = new HashSet();
ContentItem draftItem = item.getDraftVersion();
DataCollection linksToItem = session.retrieve(BASE_DATA_OBJECT_TYPE);
linksToItem.addEqualsFilter(DRAFT_TARGET + ".id", draftItem.getID());
linksToItem.addEqualsFilter(SOURCE_MASTER_ITEM + "."
+ ContentItem.VERSION, ContentItem.LIVE);
updateLiveLinksFromCollection(linksToItem, itemsToRefresh);
linksToItem.close();
DataCollection linksFromItem = session.retrieve(BASE_DATA_OBJECT_TYPE);
linksFromItem.addEqualsFilter(SOURCE_MASTER_ITEM + ".id", item.getID());
updateLiveLinksFromCollection(linksFromItem, null);
linksFromItem.close();
Iterator refreshIterator = itemsToRefresh.iterator();
while (refreshIterator.hasNext()) {
OID oid = (OID) refreshIterator.next();
ContentItem refreshItem = (ContentItem) DomainObjectFactory.
newInstance(oid);
if (refreshItem.canPublishToFS()) {
QueueManager.queueRepublish(refreshItem);
}
}
}
private static void updateLiveLinksFromCollection(DataCollection coll,
Set itemsToRefresh) {
while (coll.next()) {
// will change w/ OID references
DataObject master = (DataObject) coll.get(SOURCE_MASTER_ITEM);
DataObject src = (DataObject) coll.get(PENDING_SOURCE);
src.specialize((String) src.get(ACSObject.OBJECT_TYPE));
String propertyName = (String) coll.get(PROPERTY_NAME);
byte[] linkAttributes = (byte[]) coll.get(LINK_ATTRIBUTES);
Assert.exists(src, DataObject.class);
Assert.exists(propertyName, String.class);
DataObject target = null;
DataObject draftTarget = (DataObject) coll.get(DRAFT_TARGET);
DataAssociationCursor targetVersions =
((DataAssociation) draftTarget.get(
ContentItem.VERSIONS)).cursor();
targetVersions.addEqualsFilter(ContentItem.VERSION, ContentItem.LIVE);
if (targetVersions.next()) {
target = targetVersions.getDataObject();
}
targetVersions.close();
if (target != null) {
ObjectType ot = src.getObjectType();
Property prop = ot.getProperty(propertyName);
Assert.exists(prop, propertyName + " for type " + ot.
getQualifiedName() + ", ID: " + src.get("id"));
if (prop.isCollection()) {
DataAssociation da = (DataAssociation) src.get(propertyName);
setLinkAttributesForLiveLink(da.add(target), linkAttributes);
} else {
src.set(propertyName, target);
}
if (itemsToRefresh != null && master != null) {
itemsToRefresh.add(master.getOID());
}
}
}
}
/**
* At unpublish time, for links to this unpublished item, the
* source of the link is refreshed via
* QueueManager.queueRepublish.
*
* @param item The item which was just published
*/
public static void refreshOnUnpublish(ContentItem item) {
Session session = SessionManager.getSession();
Set itemsToRefresh = new HashSet();
DataCollection linksToItem = session.retrieve(BASE_DATA_OBJECT_TYPE);
linksToItem.addEqualsFilter(DRAFT_TARGET + ".id", item.getID());
linksToItem.addEqualsFilter(SOURCE_MASTER_ITEM + "."
+ ContentItem.VERSION, ContentItem.LIVE);
while (linksToItem.next()) {
// will change w/ OID references
DataObject master = (DataObject) linksToItem.get(SOURCE_MASTER_ITEM);
if (master != null) {
itemsToRefresh.add(master.getOID());
}
}
Iterator refreshIterator = itemsToRefresh.iterator();
while (refreshIterator.hasNext()) {
OID oid = (OID) refreshIterator.next();
ContentItem refreshItem = (ContentItem) DomainObjectFactory.
newInstance(oid);
if (refreshItem.canPublishToFS()) {
QueueManager.queueRepublish(refreshItem);
}
}
}
private void saveLinkAttributes(DataObject linkObj) {
Iterator properties = linkObj.getObjectType().getDeclaredProperties();
HashMap linkAttributes = new HashMap();
while (properties.hasNext()) {
Property prop = (Property) properties.next();
String key = prop.getName();
// Teste Property: Es darf kein Key und muß ein simples Attribute sein
if (prop.isAttribute() && !prop.isKeyProperty()) {
Object value = linkObj.get(key);
linkAttributes.put(key, value);
}
}
if (linkAttributes.size() > 0) {
ByteArrayOutputStream data = new ByteArrayOutputStream();
try {
ObjectOutputStream out = new ObjectOutputStream(data);
out.writeObject(linkAttributes);
} catch (IOException ex) {
throw new UncheckedWrapperException(ex);
}
set(LINK_ATTRIBUTES, data.toByteArray());
}
}
private static void setLinkAttributesForLiveLink(DataObject link,
byte[] linkAttributes) {
if (linkAttributes != null) {
ByteArrayInputStream data = null;
ObjectInputStream in = null;
HashMap attributes = null;
data = new ByteArrayInputStream(linkAttributes);
try {
in = new ObjectInputStream(data);
try {
attributes = (HashMap) in.readObject();
} catch (ClassNotFoundException ex) {
s_log.error("Class HashMap not found? WTF?");
return;
}
} catch (IOException ex) {
s_log.error("Can't read HashMap from database");
return;
}
if (attributes != null) {
Iterator keys = attributes.keySet().iterator();
while (keys.hasNext()) {
String propertyName = (String) keys.next();
Object value = (Object) attributes.get(propertyName);
if (link.getObjectType().hasDeclaredProperty(propertyName)
&& link.getSession() != null) {
link.set(propertyName, value);
}
}
}
}
}
}