blob: df6ca5b02091395ffa3823a7960a157c6815fac6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.lenya.cms.repository;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.TraversableSource;
import org.apache.lenya.ac.Identity;
import org.apache.lenya.ac.User;
import org.apache.lenya.cms.metadata.MetaData;
import org.apache.lenya.cms.metadata.MetaDataException;
import org.apache.lenya.cms.observation.DocumentEvent;
import org.apache.lenya.cms.observation.RepositoryEvent;
import org.apache.lenya.cms.observation.RepositoryEventFactory;
import org.apache.lenya.cms.rc.CheckInEntry;
import org.apache.lenya.cms.rc.RCML;
import org.apache.lenya.cms.rc.RevisionControlException;
import org.apache.lenya.transaction.Lock;
import org.apache.lenya.transaction.TransactionException;
import org.apache.lenya.transaction.Transactionable;
/**
* A repository node.
*
* @version $Id$
*/
public class SourceNode extends AbstractLogEnabled implements Node, Transactionable {
protected ServiceManager manager;
private ContentSourceWrapper contentSource;
private MetaSourceWrapper metaSource;
/**
* Ctor.
*
* @param session
* @param sourceUri
* @param manager
* @param logger
*/
public SourceNode(Session session, String sourceUri, ServiceManager manager, Logger logger) {
this.manager = manager;
enableLogging(logger);
this.session = session;
this.contentSource = new ContentSourceWrapper(this, sourceUri, manager, logger);
this.metaSource = new MetaSourceWrapper(this, sourceUri, manager, logger);
}
protected ContentSourceWrapper getContentSource() {
return this.contentSource;
}
protected MetaSourceWrapper getMetaSource() {
return this.metaSource;
}
/**
* @see org.apache.lenya.transaction.Transactionable#deleteTransactionable()
*/
public void deleteTransactionable() throws RepositoryException {
this.contentSource.deleteTransactionable();
this.metaSource.deleteTransactionable();
getRcml().delete();
}
protected String getUserId() {
String userId = null;
Identity identity = getSession().getIdentity();
if (identity != null) {
User user = identity.getUser();
if (user != null) {
userId = user.getId();
}
}
return userId;
}
/**
* @see org.apache.lenya.transaction.Transactionable#checkin()
*/
public void checkin() throws RepositoryException {
RCML rcml = getRcml();
synchronized (rcml) {
try {
if (!rcml.isCheckedOutBySession(getSession())) {
throw new RepositoryException("Cannot check in node [" + getSourceURI()
+ "]: not checked out by this session!");
}
rcml.checkIn(this, exists(), getSession().isDirty(this));
} catch (Exception e) {
throw new RepositoryException(e);
}
}
}
public void forceCheckIn() throws RepositoryException {
RCML rcml = getRcml();
synchronized (rcml) {
try {
if (!rcml.isCheckedOut()) {
throw new RepositoryException("Cannot check in node [" + getSourceURI()
+ "]: not checked out!");
}
rcml.checkIn(this, false, false);
} catch (Exception e) {
throw new RepositoryException(e);
}
}
}
/**
* @see org.apache.lenya.transaction.Transactionable#isCheckedOut()
*/
public boolean isCheckedOut() throws RepositoryException {
try {
return getRcml().isCheckedOut();
} catch (Exception e) {
throw new RepositoryException(e);
}
}
public boolean isCheckedOutBySession(Session session) throws RepositoryException {
try {
return getRcml().isCheckedOutBySession(session);
} catch (RevisionControlException e) {
throw new RepositoryException(e);
}
}
public void checkout(boolean restrictedToSession) throws RepositoryException {
if (getLogger().isDebugEnabled())
getLogger().debug("SourceNode::checkout() called, sourceURI [" + getSourceURI() + "]");
RCML rcml = getRcml();
synchronized (rcml) {
try {
if (rcml.isCheckedOut() && !rcml.isCheckedOutBySession(getSession())) {
throw new RepositoryException("The node [" + this
+ "] is already checked out by another session!");
}
if (!rcml.isCheckedOut()) {
rcml.checkOut(this, restrictedToSession);
}
} catch (RevisionControlException e) {
throw new RepositoryException(e);
}
}
}
public void checkout() throws RepositoryException {
checkout(true);
}
private Lock lock;
/**
* @see org.apache.lenya.transaction.Transactionable#hasChanged()
*/
public boolean hasChanged() throws RepositoryException {
try {
int currentVersion = getCurrentRevisionNumber();
int lockVersion = getLock().getVersion();
return currentVersion > lockVersion;
} catch (Exception e) {
throw new RepositoryException(e);
}
}
protected int getCurrentRevisionNumber() throws RepositoryException {
CheckInEntry entry;
try {
entry = getRcml().getLatestCheckInEntry();
} catch (RevisionControlException e) {
throw new RepositoryException(e);
}
if (entry == null) {
return 0;
} else {
return entry.getVersion();
}
}
/**
* @return The document node, if this is a meta data node, or the node itself otherwise.
* @throws ServiceException
* @throws RepositoryException
*/
protected Node getDocumentNode() throws ServiceException, RepositoryException {
Node node;
String sourceUri = getSourceURI();
if (sourceUri.endsWith(".meta")) {
String documentSourceUri = sourceUri
.substring(0, sourceUri.length() - ".meta".length());
NodeFactory factory = null;
try {
factory = (NodeFactory) this.manager.lookup(NodeFactory.ROLE);
node = (Node) factory.buildItem(getSession(), documentSourceUri);
} finally {
if (factory != null) {
this.manager.release(factory);
}
}
} else {
node = this;
}
return node;
}
/**
* @see org.apache.lenya.transaction.Transactionable#createTransactionable()
*/
public void createTransactionable() throws RepositoryException {
}
/**
* @see org.apache.lenya.transaction.Transactionable#lock()
*/
public synchronized void lock() throws RepositoryException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Locking [" + this + "]");
}
try {
int currentRev = getCurrentRevisionNumber();
int contentLoadRev = getContentSource().getLoadRevision();
int contentRev = contentLoadRev == -1 ? currentRev : contentLoadRev;
int metaLoadRev = getMetaSource().getLoadRevision();
int metaRev = metaLoadRev == -1 ? currentRev : metaLoadRev;
int lockRev = Math.min(contentRev, metaRev);
this.lock = getSession().createLock(this, lockRev);
} catch (TransactionException e) {
throw new RepositoryException(e);
}
}
/**
* @see org.apache.lenya.transaction.Transactionable#getLock()
*/
public Lock getLock() {
return this.lock;
}
/**
* @see org.apache.lenya.transaction.Transactionable#unlock()
*/
public void unlock() throws RepositoryException {
this.lock = null;
try {
getSession().removeLock(this);
} catch (TransactionException e) {
throw new RepositoryException(e);
}
}
/**
* @see org.apache.lenya.transaction.Transactionable#isLocked()
*/
public boolean isLocked() throws RepositoryException {
return this.lock != null;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "node " + getSourceURI();
}
/**
*
*/
public Collection getChildren() throws RepositoryException {
SourceResolver resolver = null;
TraversableSource source = null;
try {
resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
source = (TraversableSource) resolver.resolveURI(this.contentSource.getRealSourceUri());
Collection children = source.getChildren();
java.util.Iterator iterator = children.iterator();
java.util.Vector newChildren = new java.util.Vector();
while (iterator.hasNext()) {
TraversableSource child = (TraversableSource) iterator.next();
newChildren.add(new SourceNode(getSession(),
getSourceURI() + "/" + child.getName(), this.manager, getLogger()));
}
return newChildren;
} catch (Exception e) {
throw new RepositoryException(e);
}
}
/**
*
*/
public boolean isCollection() throws RepositoryException {
SourceResolver resolver = null;
TraversableSource source = null;
try {
resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
source = (TraversableSource) resolver.resolveURI(this.contentSource.getRealSourceUri());
return source.isCollection();
} catch (Exception e) {
throw new RepositoryException(e);
}
}
/**
* @see org.apache.lenya.cms.repository.Node#getSourceURI()
*/
public String getSourceURI() {
return this.contentSource.getSourceUri();
}
private Session session;
/**
* @see org.apache.lenya.cms.repository.Node#getSession()
*/
public Session getSession() {
return this.session;
}
public void registerDirty() throws RepositoryException {
try {
if (!getSession().isDirty(this)) {
getSession().registerDirty(this);
enqueueEvent(DocumentEvent.CHANGED);
}
} catch (TransactionException e) {
throw new RepositoryException(e);
}
}
protected void enqueueEvent(Object descriptor) {
RepositoryEvent event = RepositoryEventFactory.createEvent(this.manager, this, getLogger(),
descriptor);
getSession().enqueueEvent(event);
}
public void registerRemoved() throws RepositoryException {
try {
getSession().registerRemoved(this);
enqueueEvent(DocumentEvent.REMOVED);
} catch (Exception e) {
throw new RepositoryException(e);
}
}
private RCML rcml;
private Persistable persistable;
protected synchronized RCML getRcml() {
if (this.rcml == null) {
SourceNodeRcmlFactory factory = SourceNodeRcmlFactory.getInstance();
this.rcml = factory.getRcml(this, this.manager);
}
return this.rcml;
}
public History getHistory() {
return new SourceNodeHistory(this, this.manager, getLogger());
}
public MetaData getMetaData(String namespaceUri) throws MetaDataException {
return this.metaSource.getMetaData(namespaceUri);
}
public boolean exists() throws RepositoryException {
return this.contentSource.exists();
}
public OutputStream getOutputStream() throws RepositoryException {
return this.contentSource.getOutputStream();
}
public long getContentLength() throws RepositoryException {
return this.contentSource.getContentLength();
}
public InputStream getInputStream() throws RepositoryException {
return this.contentSource.getInputStream();
}
public long getLastModified() throws RepositoryException {
if (!exists()) {
throw new RepositoryException("The node [" + this + "] does not exist!");
}
long contentLastModified = this.contentSource.getLastModified();
long metaLastModified = 0;
if (this.metaSource.exists()) {
metaLastModified = this.metaSource.getLastModified();
}
return Math.max(contentLastModified, metaLastModified);
}
public String getMimeType() throws RepositoryException {
return this.contentSource.getMimeType();
}
public String[] getMetaDataNamespaceUris() throws MetaDataException {
return this.metaSource.getMetaDataNamespaceUris();
}
public synchronized void saveTransactionable() throws TransactionException {
if (!isCheckedOut()) {
throw new RepositoryException("Cannot save node [" + getSourceURI()
+ "]: not checked out!");
}
this.contentSource.saveTransactionable();
this.metaSource.saveTransactionable();
}
public void delete() throws RepositoryException {
this.contentSource.delete();
this.metaSource.delete();
registerRemoved();
}
public String getCheckoutUserId() throws RepositoryException {
RCML rcml = getRcml();
synchronized (rcml) {
try {
if (!rcml.isCheckedOut()) {
throw new RepositoryException("The node [" + this + "] is not checked out!");
}
return rcml.getLatestEntry().getIdentity();
} catch (RevisionControlException e) {
throw new RepositoryException(e);
}
}
}
public void copyRevisionsFrom(Node source) throws RepositoryException {
try {
boolean wasLocked = isLocked();
if (wasLocked) {
unlock();
}
getRcml().copyFrom(this, source);
if (wasLocked) {
// this is a hack: update the lock revision to the latest copied revision to avoid
// the "node has changed" error
this.lock = getSession().createLock(this, getCurrentRevisionNumber());
}
} catch (RevisionControlException e) {
throw new RepositoryException(e);
} catch (TransactionException e) {
throw new RepositoryException(e);
}
}
public void rollback(int revisionNumber) throws RepositoryException {
try {
long time = getHistory().getRevision(revisionNumber).getTime();
getRcml().restoreBackup(this, time);
} catch (RevisionControlException e) {
throw new RepositoryException(e);
}
}
public boolean isCheckedOutBySession() throws TransactionException {
return isCheckedOutBySession(getSession());
}
public void setPersistable(Persistable item) throws RepositoryException {
this.persistable = item;
}
public Persistable getPersistable() {
return this.persistable;
}
}