/*
* Copyright (c) 2006, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.carbon.registry.cmis;
//import edu.emory.mathcs.backport.java.util.Arrays;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.registry.cmis.util.CMISConstants;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Arrays;
/**
* Instances of this class represent a versionable cmis:document and its versions backed by an underlying
* Registry <code>Node</code>.
*/
public abstract class RegistryVersionBase extends RegistryDocument {
private static final Logger log = LoggerFactory.getLogger(RegistryVersionBase.class);
protected RegistryVersionBase(Registry repository, Resource node, RegistryTypeManager typeManager, PathManager pathManager) {
super(repository, node, typeManager, pathManager);
}
/**
* See CMIS 1.0 section 2.2.7.6 getAllVersions
*/
public Iterator<RegistryVersion> getVersions() {
try {
String[] versionArray = getRepository().getVersions(getNode().getPath());
List<String> versions;
if(versionArray.length == 0){
//Get the base node
versions = new ArrayList<String>();
versions.add(getNode().getPath());
} else {
//int endIndex = versionArray.length-1;
//skip base node
//versionArray = (String [])Arrays.copyOfRange(versionArray, 0, endIndex-1);
versions = new ArrayList<String>( Arrays.asList(versionArray) );
}
//Collections.reverse(versions); //sort();
final Iterator<String> iterator = versions.iterator();
return new Iterator<RegistryVersion>() {
public boolean hasNext() {
return iterator.hasNext();
}
public RegistryVersion next() {
String nextVersion = iterator.next();
try {
return new RegistryVersion(getRepository(), getRepository().get(nextVersion), nextVersion, typeManager, pathManager);
} catch (RegistryException e) {
throw new CmisRuntimeException("Error iterating over versions");
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
catch (RegistryException e) {
String msg = "Failed to get versions";
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
@Override
public void delete(boolean allVersions, boolean isPwc) {
Resource node = getNode();
try {
//2nd condition exists to check whether this is the original doc or the pwc
//Also checks for a Doc created AS a PWC
if (isCheckedOut(node) && !node.getPath().endsWith("_pwc")) {
if (isPwc) {
cancelCheckout(getRepository(), node);
} else {
throw new CmisStorageException("Cannot delete checked out document: " + node.getId());
}
} else if (allVersions) {
//checkout(getRepository(), node);
String path = node.getPath();
//delete all versions
String[] versions = getRepository().getVersions(path);
if(versions!=null){
for(String version: versions){
int beginIndex = version.indexOf(":")+1;
int endIndex = version.length();
String versionNumber = version.substring(beginIndex,endIndex);
long snapshotId = Long.parseLong(versionNumber);
getRepository().removeVersionHistory(path, snapshotId);
}
}
//Delete major resource TODO- check whether this has to execute
getRepository().delete(path);
} else {
//Delete the specific version
//Permanent path = /abc/def/resourceName;version=xxxx
String resourcePath = node.getPath();
String permanentPath = node.getPermanentPath();
int beginIndex = permanentPath.indexOf(":")+1;
int endIndex = permanentPath.length();
String versionNumber = permanentPath.substring(beginIndex,endIndex);
long snapshotId = Long.parseLong(versionNumber);
getRepository().removeVersionHistory(resourcePath, snapshotId);
}
}
catch (RegistryException e) {
String msg = "Failed to delete the node with path " + node.getPath();
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.7.1 checkOut
*
* @throws CmisRuntimeException
*/
public RegistryPrivateWorkingCopy checkout() {
Resource node = getNode();
try {
if (isCheckedOut(node)) {
throw new CmisConstraintException("Document is already checked out " + node.getId());
}
return getPwc(checkout(getRepository(), node));
}
catch (RegistryException e) {
String msg = "Failed checkout the node with path " + node.getPath();
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.7.3 checkedIn
*
* @throws CmisRuntimeException
*/
public RegistryVersion checkin(Properties properties, ContentStream contentStream, String checkinComment) {
Resource node = getNode();
try {
if (!isCheckedOut(node)) {
throw new CmisStorageException("Not checked out: " + node.getId());
}
if (properties != null && !properties.getPropertyList().isEmpty()) {
updateProperties(properties);
}
if (contentStream != null) {
setContentStream(contentStream, true);
}
// todo handle checkinComment
Resource resource = checkin();
String pathOfLatestVersion = getRepository().getVersions(resource.getPath())[0];
return new RegistryVersion(getRepository(), resource, pathOfLatestVersion, typeManager, pathManager);
}
catch (RegistryException e) {
String msg = "Failed checkin";
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.7.2 cancelCheckout
*
* @throws CmisRuntimeException
*/
public void cancelCheckout() {
Resource node = getNode();
try {
//TODO If node is the original copy then the pwc has to be passed!
cancelCheckout(getRepository(),node);
}
catch (RegistryException e) {
String msg = "Failed cancelling the checkout";
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* Get the private working copy of the versions series or throw an exception if not checked out.
*
* @return a {@link RegistryPrivateWorkingCopy} instance
* @throws CmisObjectNotFoundException if not checked out
* @throws CmisRuntimeException
*/
public RegistryPrivateWorkingCopy getPwc(Resource node) {
if (node.getPath().endsWith("_pwc")) {
return new RegistryPrivateWorkingCopy(getRepository(), node, typeManager, pathManager);
} else {
throw new CmisObjectNotFoundException("Not checked out document has no private working copy");
}
}
public RegistryPrivateWorkingCopy getPwc() {
Resource node = getNode();
if (isCheckedOut(node)) {
return new RegistryPrivateWorkingCopy(getRepository(), node, typeManager, pathManager);
} else {
throw new CmisObjectNotFoundException("Not checked out document has no private working copy");
}
}
/**
* Get a specific version by name
* @param name name of the version to get
* @return a {@link RegistryVersion} instance for <code>name</code>
* @throws CmisObjectNotFoundException if a version <code>name</code> does not exist
* @throws CmisRuntimeException
*/
public RegistryVersion getVersion(String name) {
try {
Resource node = getNode();
String[] versions = getRepository().getVersions(node.getPath());
if(versions == null){
throw new CmisObjectNotFoundException("No versions exist");
}
String gotVersion = null;
for (String version: versions){
if(version.equals(name)){
gotVersion = version;
}
}
if(gotVersion == null){
throw new CmisObjectNotFoundException("No version found");
}
return new RegistryVersion(getRepository(), node, gotVersion, typeManager, pathManager);
}
catch (RegistryException e) {
String msg = "Unable to get the version for " + name;
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* @return Id of the version representing the base of this versions series
* @throws RegistryException
*/
protected String getBaseNodeId() throws RegistryException {
String[] versions = getRepository().getVersions(getNode().getPath());
return (versions.length != 0 ? versions[versions.length-1] : getNode().getPath());
}
/**
* @return Id of the private working copy of this version series
* @throws RegistryException
*/
protected String getPwcId() throws RegistryException {
return null; //// WHAT the heck we return
}
@Override
protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
throws RegistryException {
super.compileProperties(properties, filter, objectInfo);
objectInfo.setWorkingCopyOriginalId(getBaseNodeId());
objectInfo.setWorkingCopyId(getPwcId());
}
@Override
protected Set<Action> compileAllowableActions(Set<Action> aas) {
Set<Action> result = super.compileAllowableActions(aas);
setAction(result, Action.CAN_GET_ALL_VERSIONS, true);
try {
if(isCheckedOut()){
setAction(result, Action.CAN_CANCEL_CHECK_OUT, true);
setAction(result, Action.CAN_CHECK_IN, true);
setAction(result, Action.CAN_CHECK_OUT, false);
} else{
//setAction(result, Action.CAN_CANCEL_CHECK_OUT, false);
//setAction(result, Action.CAN_CHECK_IN, false);
setAction(result, Action.CAN_CHECK_OUT, true);
}
} catch (RegistryException e) {
log.error("Failed compiling allowable actions ", e); //To change body of catch statement use File | Settings | File Templates.
}
return result;
}
@Override
protected String getTypeIdInternal() {
return RegistryTypeManager.DOCUMENT_TYPE_ID;
}
@Override
protected boolean isCheckedOut() throws RegistryException {
return isCheckedOut(getNode());
}
@Override
protected String getCheckedOutId() throws RegistryException {
if (isCheckedOut()){
String property = getNode().getProperty(CMISConstants.GREG_CREATED_AS_PWC);
if(property != null && property.equals("true")){
return getVersionSeriesId();
} else {
return getVersionSeriesId() + CMISConstants.PWC_SUFFIX;
}
}
return null;
}
@Override
protected String getCheckedOutBy() throws RegistryException {
return isCheckedOut()
? getNode().getProperty(CMISConstants.GREG_CHECKED_OUT_BY)
: null;
}
public static Resource checkout(Registry repository, Resource node) throws RegistryException {
/*
* When checked out, the file structure is as below
*
* Original Document: /abc/def/resourceName
* Checked out Doc : /abc/def/resourceName_pwc
*
* */
//TODO get User Name from context object
node.setProperty(CMISConstants.GREG_CHECKED_OUT_BY, "user");
repository.put(node.getPath(), node);
//Make a private working copy (/resourceName_pwc)
String destPath = node.getPath() + CMISConstants.PWC_SUFFIX;
repository.copy(node.getPath(), destPath);
node.setProperty(CMISConstants.GREG_IS_CHECKED_OUT, "true");
repository.put(node.getPath(), node);
//put the checked out doc path in checkedOut tracker
Resource resource = null;
if(repository.resourceExists(CMISConstants.GREG_CHECKED_OUT_TRACKER)){
resource = repository.get(CMISConstants.GREG_CHECKED_OUT_TRACKER);
} else{
resource = repository.newResource();
//Have to set content, otherwise Greg will throw exception when browsing this file in Workbench
resource.setContent(CMISConstants.TEMP_CONTENT);
}
resource.setProperty(destPath, "true");
repository.put(CMISConstants.GREG_CHECKED_OUT_TRACKER, resource);
return repository.get(destPath);
}
//------------------------------------------< private >---
// TODO REPLACE +++++++++++++++++++++++++++++++++++++++++
private static String getResourceName(String path) {
if(path.equals("/")){
return "/";
}
String[] parts = path.split("/");
if (parts == null) {
return path;
} else{
return parts[parts.length-1];
}
}
private Resource checkin() throws RegistryException {
getNode().setProperty(CMISConstants.GREG_IS_CHECKED_OUT, "false");
if(getNode().getProperty(CMISConstants.GREG_CHECKED_OUT_BY) != null ){
getNode().removeProperty(CMISConstants.GREG_CHECKED_OUT_BY);
}
getRepository().put(getNode().getPath(), getNode());
String nodePath = getNode().getPath();
if(nodePath.endsWith(CMISConstants.PWC_SUFFIX)){
//Remove checkedOut doc from tracker
Resource resource = getRepository().resourceExists(CMISConstants.GREG_CHECKED_OUT_TRACKER)
? getRepository().get(CMISConstants.GREG_CHECKED_OUT_TRACKER)
: null;
if(resource!=null){
if(resource.getProperty(nodePath)!=null){
resource.removeProperty(nodePath);
getRepository().put(resource.getPath(), resource);
} else{
throw new CmisRuntimeException("Checked out doc not in tracker!");
}
} else{
throw new CmisRuntimeException("Tracker not found");
}
String destPath = nodePath.substring(0, nodePath.indexOf(CMISConstants.PWC_SUFFIX));
getRepository().move(nodePath, destPath);
//Create a version
getRepository().createVersion(destPath);
//get the latest version
String pathOfLatestVersion = getRepository().getVersions(destPath)[0];
return getRepository().get(pathOfLatestVersion);
} else{
//This code is run when a newly created document is checked in
//create base version
getRepository().createVersion(getNode().getPath());
//String pathOfLatestVersion = getRepository().getVersions(getNode().getPath())[0];
return getNode();
}
}
private static void cancelCheckout(Registry repository, Resource node) throws RegistryException {
/*
* 2.2.7.2 cancelCheckOut
Description: Reverses the effect of a check-out. Removes the private working copy of the checked-out
document, allowing other documents in the version series to be checked out again. If the private working
copy has been created by createDocument, cancelCheckOut MUST delete the created document.
*
* TODO Think on this later
*/
//Remove from tracker
//Remove checkedOut doc from tracker
Resource tracker = repository.resourceExists(CMISConstants.GREG_CHECKED_OUT_TRACKER)
? repository.get(CMISConstants.GREG_CHECKED_OUT_TRACKER)
: null;
if(tracker!=null){
if(tracker.getProperty(node.getPath())!=null){
tracker.removeProperty(node.getPath());
repository.put(tracker.getPath(), tracker);
} else{
throw new CmisRuntimeException("Checked out doc not in tracker!");
}
} else{
throw new CmisRuntimeException("Tracker not found");
}
if(node.getPath().endsWith(CMISConstants.PWC_SUFFIX)){
/*
* Path of original copy : /abc/def/resourceName
* Path of checked out copy : /abc/def/resourceName_pwc
*
* node==checked out doc
* */
//Get the original copy
String pathOfPwc = node.getPath();
String pathOfOriginalCopy = pathOfPwc.substring(0, pathOfPwc.indexOf(CMISConstants.PWC_SUFFIX));
Resource resource = repository.get(pathOfOriginalCopy);
//Reset properties
//Allow the version series to be checked out again
resource.setProperty(CMISConstants.GREG_IS_CHECKED_OUT, "false");
if(resource.getProperty(CMISConstants.GREG_CHECKED_OUT_BY) != null ){
resource.removeProperty(CMISConstants.GREG_CHECKED_OUT_BY);
}
repository.put(pathOfOriginalCopy, resource);
//delete the pwc
repository.delete(node.getPath());
} else{
//Document created as a pwc
repository.delete(node.getPath());
}
}
//----Checks if isCheckedOut property exists and true
private boolean isCheckedOut(Resource node){
if(node.getPath().endsWith(CMISConstants.PWC_SUFFIX)){
String path = node.getPath();
try {
node = getRepository().get( path.substring(0, path.indexOf(CMISConstants.PWC_SUFFIX)));
} catch (RegistryException e) {
String msg = "Failed to retrieve the resource at " + path;
log.error(msg, e);
throw new CmisObjectNotFoundException(msg, e);
}
}
String property = node.getProperty(CMISConstants.GREG_IS_CHECKED_OUT);
return (property != null && property.equals("true"));
}
}