/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/StandardRDBMSAdapter.java,v 1.32.2.4 2004/11/30 07:08:36 masonjm Exp $
* $Revision: 1.32.2.4 $
* $Date: 2004/11/30 07:08:36 $
*
* ====================================================================
*
* Copyright 1999-2003 The Apache Software Foundation
*
* Licensed 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.slide.store.impl.rdbms;
import org.apache.slide.common.*;
import org.apache.slide.content.*;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.security.NodePermission;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.logger.Logger;
import java.io.*;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//import java.sql.Statement;
import java.util.*;
import java.util.Date;
/**
* J2EENodeStore Implementation based on the modified NewSlide Schema.
* This is combined store of DescriptorStore and ContentStore
* This combined store is necessary to make every operation behave in
* one transaction.
*
* @version $Revision: 1.32.2.4 $
*/
public class StandardRDBMSAdapter extends AbstractRDBMSAdapter {
protected static final String LOG_CHANNEL = StandardRDBMSAdapter.class.getName();
protected boolean bcompress;
public StandardRDBMSAdapter(Service service, Logger logger) {
super(service, logger);
bcompress = false;
}
public void setParameters(Hashtable parameters)
throws ServiceParameterErrorException, ServiceParameterMissingException {
try {
bcompress = "true".equalsIgnoreCase((String) parameters.get("compress"));
if (bcompress)
getLogger().log("Switching on content compression", LOG_CHANNEL, Logger.INFO);
super.setParameters(parameters);
} catch (Exception e) {
getLogger().log(e.toString(), LOG_CHANNEL, Logger.ERROR);
}
}
// Object
public void createObject(Connection connection, Uri uri, ObjectNode object)
throws ObjectAlreadyExistsException, ServiceAccessException {
if (!storeObject(connection, uri, object, true))
throw new ObjectAlreadyExistsException(uri.toString());
}
public void storeObject(Connection connection, Uri uri, ObjectNode object)
throws ObjectNotFoundException, ServiceAccessException {
if (!storeObject(connection, uri, object, false))
throw new ObjectNotFoundException(uri.toString());
}
protected boolean storeObject(Connection connection, Uri uri, ObjectNode object, boolean create)
throws ServiceAccessException {
String className = object.getClass().getName();
long uriid;
try {
PreparedStatement statement = null;
ResultSet res = null;
try {
uriid = assureUriId(connection, uri.toString());
statement =
connection.prepareStatement(
"select 1 from OBJECT o, URI u where o.URI_ID=u.URI_ID and u.URI_STRING=?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
if (res.next()) {
if (create)
return false;
} else {
if (!create)
return false;
}
} finally {
close(statement, res);
}
if (create) {
// create object in database
try {
statement = connection.prepareStatement("insert into OBJECT (URI_ID,CLASS_NAME) values (?,?)");
statement.setLong(1, uriid);
statement.setString(2, className);
statement.executeUpdate();
} finally {
close(statement);
}
}
// update binding...
try {
clearBinding(connection, uri);
} catch (ObjectNotFoundException e1) {
// clear only if it existed
}
Enumeration bindings = object.enumerateBindings();
while (bindings.hasMoreElements()) {
ObjectNode.Binding binding = (ObjectNode.Binding) bindings.nextElement();
try {
statement =
connection.prepareStatement(
"insert into BINDING (URI_ID, NAME, CHILD_UURI_ID) select ?, ?, URI_ID from URI where URI_STRING = ?");
statement.setLong(1, uriid);
statement.setString(2, binding.getName());
statement.setString(3, binding.getUuri());
statement.executeUpdate();
} finally {
close(statement);
}
}
Enumeration parentBindings = object.enumerateParentBindings();
while (parentBindings.hasMoreElements()) {
ObjectNode.ParentBinding parentBinding = (ObjectNode.ParentBinding) parentBindings.nextElement();
try {
statement =
connection.prepareStatement(
"insert into PARENT_BINDING (URI_ID, NAME, PARENT_UURI_ID) select ?, ?, URI_ID from URI where URI_STRING = ?");
statement.setLong(1, uriid);
statement.setString(2, parentBinding.getName());
statement.setString(3, parentBinding.getUuri());
statement.executeUpdate();
} finally {
close(statement);
}
}
if (object instanceof LinkNode) {
// update link target
try {
statement = connection.prepareStatement("delete from LINKS where URI_ID = ?");
statement.setLong(1, uriid);
statement.executeUpdate();
} finally {
close(statement);
}
try {
statement =
connection.prepareStatement(
"insert into LINKS (URI_ID, LINK_TO_ID) select ?, u.URI_ID from URI u where u.URI_STRING = ?");
statement.setLong(1, uriid);
statement.setString(2, ((LinkNode) object).getLinkedUri());
statement.executeUpdate();
} finally {
close(statement);
}
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
return true;
}
public void removeObject(Connection connection, Uri uri, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException {
PreparedStatement statement = null;
try {
clearBinding(connection, uri);
// delete links
try {
statement =
connection.prepareStatement(
"delete LINKS from LINKS l, URI u where l.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
// delete version history
// FIXME: Is this true??? Should the version history be removed if the object is removed???
try {
statement =
connection.prepareStatement(
"delete VERSION_HISTORY from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
// delete version
try {
statement =
connection.prepareStatement(
"delete VERSION from VERSION v, URI u where v.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
// delete the object itself
try {
statement =
connection.prepareStatement(
"delete OBJECT from OBJECT o, URI u where o.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
// finally delete the uri
try {
statement = connection.prepareStatement("delete from URI where URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public ObjectNode retrieveObject(Connection connection, Uri uri)
throws ServiceAccessException, ObjectNotFoundException {
ObjectNode result = null;
try {
PreparedStatement statement = null;
ResultSet res = null;
String className;
Vector children = new Vector();
Vector parents = new Vector();
Vector links = new Vector();
try {
statement =
connection.prepareStatement(
"select o.CLASS_NAME from OBJECT o, URI u where o.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
if (res.next()) {
className = res.getString(1);
} else {
throw new ObjectNotFoundException(uri);
}
} finally {
close(statement, res);
}
try {
statement =
connection.prepareStatement(
"SELECT c.NAME, cu.URI_STRING FROM URI u, URI cu, BINDING c WHERE cu.URI_ID = c.CHILD_UURI_ID AND c.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
children.addElement(new ObjectNode.Binding(res.getString(1), res.getString(2)));
}
} finally {
close(statement, res);
}
try {
statement =
connection.prepareStatement(
"SELECT c.NAME, cu.URI_STRING FROM URI u, URI cu, PARENT_BINDING c WHERE cu.URI_ID = c.PARENT_UURI_ID AND c.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
parents.addElement(new ObjectNode.ParentBinding(res.getString(1), res.getString(2)));
}
} finally {
close(statement, res);
}
try {
statement =
connection.prepareStatement(
"SELECT lu.URI_STRING FROM URI u, URI lu, LINKS l WHERE lu.URI_ID = l.URI_ID AND l.LINK_TO_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
links.addElement(res.getString(1));
}
} finally {
close(statement, res);
}
if (className.equals(LinkNode.class.getName())) {
try {
statement =
connection.prepareStatement(
"SELECT lu.URI_STRING FROM URI u, URI lu, LINKS l WHERE lu.URI_ID = l.LINK_TO_ID AND l.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
if (res.next()) {
String linkTarget = res.getString(1);
result = new LinkNode(uri.toString(), children, links, linkTarget);
} else {
result = new LinkNode(uri.toString(), children, links);
}
} finally {
close(statement, res);
}
} else {
try {
Class objclass = Class.forName(className);
Class argClasses[] = { String.class, Vector.class, Vector.class, Vector.class };
Object arguments[] = { uri.toString(), children, parents, links };
Constructor constructor = objclass.getConstructor(argClasses);
result = (ObjectNode) constructor.newInstance(arguments);
result.setUri(result.getUuri());
} catch (Exception e) {
throw new ServiceAccessException(service, e);
}
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
return result;
}
// Locks
public Enumeration enumerateLocks(Connection connection, Uri uri) throws ServiceAccessException {
Vector lockVector = new Vector();
PreparedStatement statement = null;
ResultSet res = null;
try {
statement =
connection.prepareStatement(
"select l.EXPIRATION_DATE, l.IS_INHERITABLE, l.IS_EXCLUSIVE, u2.URI_STRING as LCK, u3.URI_STRING as SUBJECT, u4.URI_STRING as TYPE, l.OWNER from LOCKS l, URI u, URI u2, URI u3, URI u4 where l.OBJECT_ID=u.URI_ID and u.URI_STRING=? and l.LOCK_ID=u2.URI_ID and l.SUBJECT_ID=u3.URI_ID and l.TYPE_ID = u4.URI_ID");
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
Date expirationDate = null;
try {
Long timeValue = new Long(res.getLong("EXPIRATION_DATE"));
expirationDate = new Date(timeValue.longValue());
} catch (NumberFormatException e) {
getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
expirationDate = new Date();
}
NodeLock lock =
new NodeLock(
res.getString("LCK"),
uri.toString(),
res.getString("SUBJECT"),
res.getString("TYPE"),
expirationDate,
res.getInt("IS_INHERITABLE") == 1,
res.getInt("IS_EXCLUSIVE") == 1,
res.getString("OWNER"));
lockVector.addElement(lock);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement, res);
}
return lockVector.elements();
}
public void killLock(Connection connection, Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
removeLock(connection, uri, lock);
}
public void putLock(Connection connection, Uri uri, NodeLock lock) throws ServiceAccessException {
PreparedStatement statement = null;
try {
int inheritable = lock.isInheritable() ? 1 : 0;
int exclusive = lock.isExclusive() ? 1 : 0;
long lockid = assureUriId(connection, lock.getLockId());
statement =
connection.prepareStatement(
"insert into LOCKS (LOCK_ID,OBJECT_ID,SUBJECT_ID,TYPE_ID,EXPIRATION_DATE,IS_INHERITABLE,IS_EXCLUSIVE,OWNER) select ?, object.URI_ID, subject.URI_ID, type.URI_ID, ?, ?, ?, ? from URI object, URI subject, URI type WHERE object.URI_STRING=? and subject.URI_STRING=? and type.URI_STRING=?");
statement.setLong(1, lockid);
statement.setLong(2, lock.getExpirationDate().getTime());
statement.setInt(3, inheritable);
statement.setInt(4, exclusive);
statement.setString(5, lock.getOwnerInfo());
statement.setString(6, lock.getObjectUri());
statement.setString(7, lock.getSubjectUri());
statement.setString(8, lock.getTypeUri());
statement.execute();
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement);
}
}
public void renewLock(Connection connection, Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
try {
PreparedStatement statement = null;
ResultSet rslt = null;
try {
statement =
connection.prepareStatement(
"select 1 from LOCKS l, URI u where l.LOCK_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, lock.getLockId());
rslt = statement.executeQuery();
if (rslt.next()) {
removeLock(connection,uri,lock);
putLock(connection, uri, lock);
} else {
throw new LockTokenNotFoundException(lock);
}
} finally {
close(statement, rslt);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void removeLock(Connection connection, Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
PreparedStatement statement = null;
try {
try {
statement =
connection.prepareStatement(
"delete LOCKS from LOCKS, URI u where LOCK_ID = u.URI_ID and u.URI_STRING=?");
statement.setString(1, lock.getLockId());
statement.executeUpdate();
} finally {
close(statement);
}
try {
statement =
connection.prepareStatement(
"delete URI from URI, LOCKS l where URI_STRING=?");
statement.setString(1, lock.getLockId());
statement.executeUpdate();
} finally {
close(statement);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
// Permissions
public Enumeration enumeratePermissions(Connection connection, Uri uri) throws ServiceAccessException {
Vector permissions = new Vector();
PreparedStatement statement = null;
ResultSet res = null;
try {
statement =
connection.prepareStatement(
"select ou.URI_STRING, su.URI_STRING, au.URI_STRING, p.VERSION_NO, p.IS_INHERITABLE, p.IS_NEGATIVE from PERMISSIONS p, URI ou, URI su, URI au where p.OBJECT_ID = ou.URI_ID and ou.URI_STRING = ? and p.SUBJECT_ID = su.URI_ID and p.ACTION_ID = au.URI_ID order by SUCCESSION");
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
String object = res.getString(1);
String subject = res.getString(2);
String action = res.getString(3);
String revision = res.getString(4);
if ("NULL".equals(revision)) {
revision = null;
}
boolean inheritable = (res.getInt(5) == 1);
boolean negative = (res.getInt(6) == 1);
NodePermission permission =
new NodePermission(object, revision, subject, action, inheritable, negative);
permissions.add(permission);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement, res);
}
return permissions.elements();
}
public void grantPermission(Connection connection, Uri uri, NodePermission permission)
throws ServiceAccessException {
PreparedStatement statement = null;
ResultSet res = null;
int succession = 0;
try {
// FIXME: This might be useless if insert inserts nothing if insert...select works a i expect
// FIXME: What happens, if only revision number, inheritable or negative changes?
// FIXME
// revokePermission(connection, uri, permission);
try {
statement =
connection.prepareStatement(
"select max(p.SUCCESSION) from URI ou, PERMISSIONS p where p.OBJECT_ID = ou.URI_ID and ou.URI_STRING = ?");
statement.setString(1, permission.getObjectUri());
res = statement.executeQuery();
res.next();
succession = res.getInt(1) + 1;
} finally {
close(statement, res);
}
assureUriId(connection,permission.getSubjectUri());
assureUriId(connection,permission.getActionUri());
try {
int inheritable = permission.isInheritable() ? 1 : 0;
int negative = permission.isNegative() ? 1 : 0;
statement =
connection.prepareStatement(
"insert into PERMISSIONS (OBJECT_ID,SUBJECT_ID,ACTION_ID,VERSION_NO, IS_INHERITABLE,IS_NEGATIVE,SUCCESSION) select ou.URI_ID, su.URI_ID, au.URI_ID, ?, ?, ?, ? from URI ou, URI su, URI au where ou.URI_STRING = ? and su.URI_STRING = ? and au.URI_STRING = ?");
statement.setString(1, getRevisionNumberAsString(permission.getRevisionNumber()));
statement.setInt(2, inheritable);
statement.setInt(3, negative);
statement.setInt(4, succession);
statement.setString(5, permission.getObjectUri());
statement.setString(6, permission.getSubjectUri());
statement.setString(7, permission.getActionUri());
if (statement.executeUpdate() != 1) {
String msg = "Failed to insert permission ("
+ permission.getObjectUri()
+ "," + permission.getSubjectUri()
+ "," + permission.getActionUri()
+ ")";
getLogger().log(msg,LOG_CHANNEL,Logger.ERROR);
}
} finally {
close(statement);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void revokePermission(Connection connection, Uri uri, NodePermission permission)
throws ServiceAccessException {
if (permission == null) return;
PreparedStatement statement = null;
try {
NodeRevisionNumber revisionNumber = permission.getRevisionNumber();
statement =
connection.prepareStatement(
"delete PERMISSIONS from PERMISSIONS, URI ou, URI su, URI au where OBJECT_ID = ou.URI_ID and ou.URI_STRING = ? and SUBJECT_ID = su.URI_ID and su.URI_STRING = ? and ACTION_ID = au.URI_ID and au.URI_STRING = ? and VERSION_NO" + ((revisionNumber == null) ? " IS NULL " : " = '" + revisionNumber.toString() + "'"));
statement.setString(1, permission.getObjectUri());
statement.setString(2, permission.getSubjectUri());
statement.setString(3, permission.getActionUri());
statement.executeUpdate();
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement);
}
}
public void revokePermissions(Connection connection, Uri uri) throws ServiceAccessException {
PreparedStatement statement = null;
try {
statement =
connection.prepareStatement(
"delete PERMISSIONS from PERMISSIONS, URI u where OBJECT_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement);
}
}
public void createRevisionDescriptor(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException {
PreparedStatement statement = null;
try {
assureVersionInfo(connection, uri, revisionDescriptor);
for (Enumeration labels = revisionDescriptor.enumerateLabels(); labels.hasMoreElements();) {
long labelId = assureLabelId(connection, (String) labels.nextElement());
try {
statement =
connection.prepareStatement(
"insert into VERSION_LABELS (VERSION_ID, LABEL_ID) select VERSION_ID, ? from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
statement.setLong(1, labelId);
statement.setString(2, uri.toString());
statement.setString(3, revisionDescriptor.getRevisionNumber().toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
for (Enumeration properties = revisionDescriptor.enumerateProperties(); properties.hasMoreElements();) {
try {
NodeProperty property = (NodeProperty) properties.nextElement();
statement =
connection.prepareStatement(
"insert into PROPERTIES (VERSION_ID,PROPERTY_NAMESPACE,PROPERTY_NAME,PROPERTY_VALUE,PROPERTY_TYPE,IS_PROTECTED) select vh.VERSION_ID, ?, ?, ?, ?, ? from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
int protectedProperty = property.isProtected() ? 1 : 0;
statement.setString(1, property.getNamespace());
statement.setString(2, property.getName());
statement.setString(3, property.getValue().toString());
statement.setString(4, property.getType());
statement.setInt(5, protectedProperty);
statement.setString(6, uri.toString());
statement.setString(7, revisionDescriptor.getRevisionNumber().toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void removeRevisionContent(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException {
try {
PreparedStatement statement = null;
try {
statement =
connection.prepareStatement(
"delete VERSION_CONTENT from VERSION_CONTENT vc, VERSION_HISTORY vh, URI u where vc.VERSION_ID = vh.VERSION_ID and vh.REVISION_NO = ? and vh.URI_ID=u.URI_ID AND u.URI_STRING=?");
statement.setString(1, revisionDescriptor.getRevisionNumber().toString());
statement.setString(2, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void removeRevisionDescriptor(Connection connection, Uri uri, NodeRevisionNumber revisionNumber)
throws ServiceAccessException {
PreparedStatement statement = null;
try {
try {
statement =
connection.prepareStatement(
"delete VERSION_LABELS from VERSION_LABELS vl, VERSION_HISTORY vh, URI u where vl.VERSION_ID = vh.VERSION_ID and vh.REVISION_NO = ? and vh.URI_ID = u.URI_ID AND u.URI_STRING = ?");
statement.setString(1, revisionNumber.toString());
statement.setString(2, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
try {
statement =
connection.prepareStatement(
"delete PROPERTIES from PROPERTIES p, VERSION_HISTORY vh, URI u where p.VERSION_ID = vh.VERSION_ID and vh.REVISION_NO = ? and vh.URI_ID = u.URI_ID AND u.URI_STRING = ?");
statement.setString(1, revisionNumber.toString());
statement.setString(2, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void removeRevisionDescriptors(Connection connection, Uri uri) throws ServiceAccessException {
PreparedStatement statement = null;
try {
statement =
connection.prepareStatement(
"delete VERSION_PREDS from VERSION_PREDS vp, VERSION_HISTORY vh, URI u where vp.VERSION_ID = vh.VERSION_ID and vh.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} catch (SQLException e) {
throw createException(e, uri.toString());
} finally {
close(statement);
}
}
public NodeRevisionContent retrieveRevisionContent(
Connection connection,
Uri uri,
NodeRevisionDescriptor revisionDescriptor,
boolean temporaryConnection)
throws ServiceAccessException, RevisionNotFoundException {
NodeRevisionContent result = null;
try {
PreparedStatement statement = null;
ResultSet res = null;
try {
statement =
connection.prepareStatement(
"select vc.CONTENT from VERSION_CONTENT vc, VERSION_HISTORY vh, URI u where vc.VERSION_ID = vh.VERSION_ID and vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, revisionDescriptor.getRevisionNumber().toString());
res = statement.executeQuery();
if (!res.next()) {
throw new RevisionNotFoundException(uri.toString(), revisionDescriptor.getRevisionNumber());
}
InputStream is = res.getBinaryStream("CONTENT");
if (is == null) {
throw new RevisionNotFoundException(uri.toString(), revisionDescriptor.getRevisionNumber());
}
result = new NodeRevisionContent();
if (bcompress) {
getLogger().log("DeCompressing the data", LOG_CHANNEL, Logger.DEBUG);
StoreContentZip ziputil = new StoreContentZip();
ziputil.UnZip(is);
revisionDescriptor.setContentLength(ziputil.getContentLength());
is = ziputil.getInputStream();
result.setContent(is);
} else {
if (temporaryConnection) {
result.setContent(new JDBCAwareInputStream(is, statement, res, connection));
} else {
result.setContent(new JDBCAwareInputStream(is, statement, res, null));
}
}
} finally {
try {
close(statement, res);
} finally {
// do not close when not compressed, as stream needs to be read
// before
if (bcompress) {
if (temporaryConnection) {
// XXX is needed, as calling store does not know if
// connection should be closed now
try {
connection.commit();
} finally {
connection.close();
}
}
}
}
}
} catch (SQLException e) {
throw createException(e, uri.toString());
} catch (RevisionNotFoundException e) {
// This is not that bad, but only means, no content available. Do not warn, as this happens frequently with users / actions
/*
getLogger().log(
"RevisionNotFoundException encountered for " + uri.toString() + " revision " + revisionDescriptor.getRevisionNumber(),
LOG_CHANNEL,
4);
*/
throw e;
} catch (Exception e) {
getLogger().log(e, LOG_CHANNEL, Logger.ERROR);
throw new ServiceAccessException(service, e);
}
return result;
}
public NodeRevisionDescriptor retrieveRevisionDescriptor(
Connection connection,
Uri uri,
NodeRevisionNumber revisionNumber)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
NodeRevisionDescriptor revisionDescriptor = null;
if (revisionNumber == null)
throw new RevisionDescriptorNotFoundException(uri.toString());
try {
PreparedStatement statement = null;
ResultSet res = null;
String branch = null;
Vector labels = new Vector();
Hashtable properties = new Hashtable();
long versionId = 0;
try {
statement =
connection.prepareStatement(
"select vh.VERSION_ID, b.BRANCH_STRING from VERSION_HISTORY vh, BRANCH b, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and b.BRANCH_ID = vh.BRANCH_ID and vh.REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, revisionNumber.toString());
res = statement.executeQuery();
if (res.next()) {
versionId = res.getLong(1);
branch = res.getString(2);
} else {
throw new RevisionDescriptorNotFoundException(uri.toString());
}
} finally {
close(statement, res);
}
try {
statement =
connection.prepareStatement(
"select l.LABEL_STRING from VERSION_LABELS vl, LABEL l where vl.VERSION_ID = ? and l.LABEL_ID = vl.LABEL_ID");
statement.setLong(1, versionId);
res = statement.executeQuery();
while (res.next()) {
labels.addElement(res.getString(1));
}
} finally {
close(statement, res);
}
try {
statement =
connection.prepareStatement(
"select PROPERTY_NAME, PROPERTY_NAMESPACE, PROPERTY_VALUE, PROPERTY_TYPE, IS_PROTECTED from PROPERTIES where VERSION_ID = ?");
statement.setLong(1, versionId);
res = statement.executeQuery();
while (res.next()) {
String propertyName = res.getString(1);
String propertyNamespace = res.getString(2);
NodeProperty property =
new NodeProperty(
propertyName,
res.getString(3),
propertyNamespace,
res.getString(4),
res.getInt(5) == 1);
properties.put(propertyNamespace + propertyName, property);
}
} finally {
close(statement, res);
}
revisionDescriptor = new NodeRevisionDescriptor(revisionNumber, branch, labels, properties);
} catch (SQLException e) {
throw createException(e, uri.toString());
}
return revisionDescriptor;
}
public NodeRevisionDescriptors retrieveRevisionDescriptors(Connection connection, Uri uri)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
NodeRevisionDescriptors revisionDescriptors = null;
PreparedStatement statement = null;
ResultSet res = null;
try {
NodeRevisionNumber initialRevision = new NodeRevisionNumber();
// FIXME: Some code might be lost: workingRevisions is not filled with values
Hashtable workingRevisions = new Hashtable();
Hashtable latestRevisionNumbers = new Hashtable();
Hashtable branches = new Hashtable();
Vector allRevisions = new Vector();
boolean isVersioned = false;
// check if versioned
try {
statement =
connection.prepareStatement(
"select IS_VERSIONED from VERSION v, URI u where v.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
if (res.next()) {
isVersioned = (res.getInt("IS_VERSIONED") == 1);
} else {
throw new RevisionDescriptorNotFoundException(uri.toString());
}
} finally {
close(statement, res);
}
// retrieve revision numbers
try {
statement =
connection
.prepareStatement(
"select vh.REVISION_NO, b.BRANCH_STRING from VERSION_HISTORY vh, BRANCH b, URI u where vh.BRANCH_ID = b.BRANCH_ID and vh.URI_ID = u.URI_ID and u.URI_STRING = ? order by "
+ convertRevisionNumberToComparable("vh.REVISION_NO"));
statement.setString(1, uri.toString());
res = statement.executeQuery();
while (res.next()) {
NodeRevisionNumber revisionNumber = new NodeRevisionNumber(res.getString(1));
allRevisions.add(revisionNumber); // will be used to get revisions successor
latestRevisionNumbers.put(res.getString(2), revisionNumber); // store the lastest revision / branch
}
} finally {
close(statement, res);
}
for (Enumeration e = allRevisions.elements(); e.hasMoreElements();) {
NodeRevisionNumber revisionNumber = (NodeRevisionNumber) e.nextElement();
// get successors
try {
statement =
connection.prepareStatement(
"select distinct pvh.REVISION_NO from VERSION_HISTORY vh, VERSION_HISTORY pvh, VERSION_PREDS vp, URI u where pvh.VERSION_ID = vp.VERSION_ID and vp.VERSION_ID = vh.VERSION_ID and vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, revisionNumber.toString());
res = statement.executeQuery();
Vector predecessors = new Vector();
while (res.next()) {
predecessors.add(new NodeRevisionNumber(res.getString(1)));
}
// XXX it is important to call ctor with String, otherwise it does an increment in minor number :(
branches.put(new NodeRevisionNumber(revisionNumber.toString()), predecessors);
} finally {
close(statement, res);
}
}
revisionDescriptors =
new NodeRevisionDescriptors(
uri.toString(),
initialRevision,
workingRevisions,
latestRevisionNumbers,
branches,
isVersioned);
} catch (SQLException e) {
throw createException(e, uri.toString());
}
return revisionDescriptors;
}
public void createRevisionDescriptors(Connection connection, Uri uri, NodeRevisionDescriptors revisionDescriptors)
throws ServiceAccessException {
PreparedStatement statement = null;
ResultSet res = null;
try {
int isVersioned = 0;
if (revisionDescriptors.isVersioned())
isVersioned = 1;
boolean revisionExists;
try {
statement =
connection.prepareStatement(
"SELECT 1 FROM VERSION v, URI u WHERE v.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
revisionExists = res.next();
} finally {
close(statement, res);
}
if (!revisionExists) {
try {
statement =
connection.prepareStatement(
"insert into VERSION (URI_ID, IS_VERSIONED) select u.URI_ID, ? from URI u where u.URI_STRING = ?");
statement.setInt(1, isVersioned);
statement.setString(2, uri.toString());
statement.executeUpdate();
} finally {
close(statement, res);
}
}
boolean versionHistoryExists;
NodeRevisionNumber versionNumber = revisionDescriptors.hasRevisions() ? revisionDescriptors.getLatestRevision() : new NodeRevisionNumber();
try {
statement =
connection.prepareStatement(
"SELECT 1 FROM VERSION_HISTORY vh, URI u WHERE vh.URI_ID = u.URI_ID and u.URI_STRING = ? and REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, versionNumber.toString());
res = statement.executeQuery();
versionHistoryExists = res.next();
} finally {
close(statement, res);
}
if (!versionHistoryExists && revisionDescriptors.getLatestRevision() != null) {
try {
statement =
connection.prepareStatement(
"insert into VERSION_HISTORY (URI_ID, BRANCH_ID, REVISION_NO) select u.URI_ID, b.BRANCH_ID, ? from URI u, BRANCH b where u.URI_STRING = ? and b.BRANCH_STRING = ?");
statement.setString(1, getRevisionNumberAsString(versionNumber));
statement.setString(2, uri.toString());
statement.setString(3, NodeRevisionDescriptors.MAIN_BRANCH);
// FIXME: Create new revisions on the main branch???
statement.executeUpdate();
} finally {
close(statement, res);
}
}
// Add revision successors
Enumeration revisionNumbers = revisionDescriptors.enumerateRevisionNumbers();
while (revisionNumbers.hasMoreElements()) {
NodeRevisionNumber nodeRevisionNumber = (NodeRevisionNumber) revisionNumbers.nextElement();
Enumeration successors = revisionDescriptors.getSuccessors(nodeRevisionNumber);
while (successors.hasMoreElements())
{
try {
NodeRevisionNumber successor = (NodeRevisionNumber) successors.nextElement();
statement =
connection.prepareStatement(
"insert into VERSION_PREDS (VERSION_ID, PREDECESSOR_ID) " +
" select vr.VERSION_ID, suc.VERSION_ID" +
" FROM URI uri, VERSION_HISTORY vr, VERSION_HISTORY suc " +
" where vr.URI_ID = uri.URI_ID " +
" and suc.URI_ID = uri.URI_ID " +
" and uri.URI_STRING = ? " +
" and vr.REVISION_NO = ? " +
" and suc.REVISION_NO = ? " );
statement.setString(1, uri.toString());
statement.setString(2, nodeRevisionNumber.toString());
statement.setString(3, successor.toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
}
getLogger().log(
revisionDescriptors.getOriginalUri() + revisionDescriptors.getInitialRevision(),
LOG_CHANNEL,
Logger.DEBUG);
} catch (SQLException e) {
throw createException(e, uri.toString());
}
}
public void createRevisionContent(
Connection connection,
Uri uri,
NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent)
throws ServiceAccessException, RevisionAlreadyExistException {
if (!storeRevisionContent(connection, uri, revisionDescriptor, revisionContent, true)) {
throw new RevisionAlreadyExistException(uri.toString(), revisionDescriptor.getRevisionNumber());
}
}
public void storeRevisionContent(
Connection connection,
Uri uri,
NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent)
throws ServiceAccessException, RevisionNotFoundException {
if (!storeRevisionContent(connection, uri, revisionDescriptor, revisionContent, false)) {
throw new RevisionNotFoundException(uri.toString(), revisionDescriptor.getRevisionNumber());
}
}
public boolean storeRevisionContent(
Connection connection,
Uri uri,
NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent,
boolean create)
throws ServiceAccessException {
try {
// check if revision already exists
PreparedStatement statement = null;
ResultSet res = null;
try {
statement =
connection.prepareStatement(
"select 1 from VERSION_CONTENT vc, VERSION_HISTORY vh, URI u where vc.VERSION_ID = vh.VERSION_ID and vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, revisionDescriptor.getRevisionNumber().toString());
res = statement.executeQuery();
if (res.next()) {
if (create)
return false;
} else {
if (!create)
return false;
}
} finally {
close(statement, res);
}
if (!create) {
removeRevisionContent(connection, uri, revisionDescriptor);
}
storeContent(connection, uri, revisionDescriptor, revisionContent);
} catch (SQLException e) {
throw createException(e, uri.toString());
} catch (IOException e) {
getLogger().log(e, LOG_CHANNEL, Logger.ERROR);
throw new ServiceAccessException(service, e);
}
return true;
}
protected void assureVersionInfo(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws SQLException {
PreparedStatement statement = null;
ResultSet res = null;
boolean revisionExists;
try {
statement =
connection.prepareStatement(
"select 1 from VERSION v, URI u where v.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
res = statement.executeQuery();
revisionExists = res.next();
} finally {
close(statement, res);
}
// FIXME: Is it true, that the default for IS_VERSIONED is 0 ??
if (!revisionExists) {
try {
statement =
connection.prepareStatement(
"insert into VERSION (URI_ID, IS_VERSIONED) select u.URI_ID, ? from URI u where u.URI_STRING = ?");
statement.setInt(1, 0);
statement.setString(2, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
boolean versionHistoryExists;
try {
statement =
connection.prepareStatement(
"select 1 from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and REVISION_NO = ?");
statement.setString(1, uri.toString());
statement.setString(2, revisionDescriptor.getRevisionNumber().toString());
res = statement.executeQuery();
versionHistoryExists = res.next();
} finally {
close(statement, res);
}
if (!versionHistoryExists) {
long branchId = assureBranchId(connection, revisionDescriptor.getBranchName());
try {
statement =
connection.prepareStatement(
"insert into VERSION_HISTORY (URI_ID, BRANCH_ID, REVISION_NO) select u.URI_ID, ?, ? from URI u where u.URI_STRING = ?");
statement.setLong(1, branchId);
statement.setString(2, getRevisionNumberAsString(revisionDescriptor.getRevisionNumber()));
statement.setString(3, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
}
protected void storeContent(
Connection connection,
Uri uri,
NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent)
throws IOException, SQLException {
assureVersionInfo(connection, uri, revisionDescriptor);
PreparedStatement statement = null;
InputStream is = null;
is = revisionContent.streamContent();
if (is != null) {
long contentLength = 0;
if (bcompress) {
getLogger().log("Compressing the data", LOG_CHANNEL, Logger.DEBUG);
StoreContentZip ziputil = new StoreContentZip();
ziputil.Zip(is);
is = ziputil.getInputStream();
contentLength = ziputil.getContentLength();
} else {
contentLength = revisionDescriptor.getContentLength();
}
byte buffer[] = new byte[2048];
File tempFile = null;
if (contentLength == -1) {
try {
tempFile = File.createTempFile("content", null);
FileOutputStream fos = new FileOutputStream(tempFile);
do {
int nChar = is.read(buffer);
if (nChar == -1)
break;
fos.write(buffer, 0, nChar);
} while (true);
fos.close();
is.close();
is = new FileInputStream(tempFile);
contentLength = tempFile.length();
} catch (IOException ex) {
getLogger().log(
ex.toString() + " during the calculation of the content length.",
LOG_CHANNEL,
Logger.ERROR);
getLogger().log("tempFile: " + tempFile.getAbsolutePath(), LOG_CHANNEL, Logger.ERROR);
throw ex;
}
}
// Changed (contentlength - 1) to (contentlength) as DB2 was complaining about
// length mismatch for stream in the below method
// insertStatement.setBinaryStream(2, is, (int)contentLength -1);
try {
statement =
connection.prepareStatement(
"insert into VERSION_CONTENT (VERSION_ID, CONTENT) select vh.VERSION_ID, ? from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO = ?");
statement.setBinaryStream(1, is, (int) contentLength);
statement.setString(2, uri.toString());
statement.setString(3, revisionDescriptor.getRevisionNumber().toString());
statement.executeUpdate();
if (tempFile != null) {
is.close();
is = null;
tempFile.delete();
}
} finally {
try {
close(statement);
} finally {
if (is != null) {
// XXX some JDBC drivers seem to close the stream upon closing of
// the statement; if so this will raise an IOException
// silently ignore it...
try {
is.close();
} catch (IOException ioe) {
logger.log("Could not close stream", ioe, LOG_CHANNEL, Logger.DEBUG);
}
}
}
}
}
}
public void storeRevisionDescriptor(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
removeRevisionDescriptor(connection, uri, revisionDescriptor.getRevisionNumber());
createRevisionDescriptor(connection, uri, revisionDescriptor);
}
public void storeRevisionDescriptors(Connection connection, Uri uri, NodeRevisionDescriptors revisionDescriptors)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
removeRevisionDescriptors(connection, uri);
createRevisionDescriptors(connection, uri, revisionDescriptors);
}
protected long assureUriId(Connection connection, String uri) throws SQLException {
PreparedStatement statement = null;
ResultSet res = null;
try {
statement = connection.prepareStatement("select URI_ID from URI where URI_STRING=?");
statement.setString(1, uri);
res = statement.executeQuery();
if (res.next()) {
long id = res.getLong(1);
return id;
}
} finally {
close(statement, res);
}
try {
statement = connection.prepareStatement("insert into URI (URI_STRING) values (?)");
statement.setString(1, uri);
statement.executeUpdate();
} finally {
close(statement);
}
return assureUriId(connection, uri);
}
protected long assureBranchId(Connection connection, String branch) throws SQLException {
PreparedStatement statement = null;
ResultSet res = null;
try {
statement = connection.prepareStatement("select BRANCH_ID from BRANCH where BRANCH_STRING=?");
statement.setString(1, branch);
res = statement.executeQuery();
if (res.next()) {
long id = res.getLong(1);
return id;
}
} finally {
close(statement, res);
}
try {
statement = connection.prepareStatement("insert into BRANCH (BRANCH_STRING) values (?)");
statement.setString(1, branch);
statement.executeUpdate();
} finally {
close(statement);
}
return assureBranchId(connection, branch);
}
protected long assureLabelId(Connection connection, String label) throws SQLException {
PreparedStatement statement = null;
ResultSet res = null;
try {
statement = connection.prepareStatement("select LABEL_ID from LABEL where LABEL_STRING=?");
statement.setString(1, label);
res = statement.executeQuery();
if (res.next()) {
long id = res.getLong(1);
return id;
}
} finally {
close(statement, res);
}
try {
statement = connection.prepareStatement("insert into LABEL (LABEL_STRING) values (?)");
statement.setString(1, label);
statement.executeUpdate();
} finally {
close(statement);
}
return assureLabelId(connection, label);
}
protected void clearBinding(Connection connection, Uri uri)
throws ServiceAccessException, ObjectNotFoundException, SQLException {
PreparedStatement statement = null;
// clear this uri from having bindings and being bound
try {
statement =
connection.prepareStatement(
"delete BINDING from BINDING c, URI u where c.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
try {
statement =
connection.prepareStatement(
"delete PARENT_BINDING from PARENT_BINDING c, URI u where c.URI_ID = u.URI_ID and u.URI_STRING = ?");
statement.setString(1, uri.toString());
statement.executeUpdate();
} finally {
close(statement);
}
}
// null means permission is valid for all revisions
protected String getRevisionNumberAsString(NodeRevisionNumber revisionNumber) {
return revisionNumber != null ? revisionNumber.toString() : null;
}
protected String convertRevisionNumberToComparable(String revisioNumber) {
return "convert(numeric, SUBSTRING("+revisioNumber+",1,charindex('.',"+revisioNumber+"))), convert(numeric, SUBSTRING("+revisioNumber+",charindex('.',"+revisioNumber+")+1,100))";
}
protected void close(PreparedStatement statement) {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
}
}
protected void close(PreparedStatement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
} finally {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
}
}
}
// overload this method to have a more detailed error handling
protected ServiceAccessException createException(SQLException e, String uri) {
getLogger().log(
"SQL error " + e.getErrorCode() + " on " + uri + ": " + e.getMessage(),
LOG_CHANNEL,
Logger.ERROR);
return new ServiceAccessException(service, e);
}
}