/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2005, ThoughtWorks, Inc.
* 200 E. Randolph, 25th Floor
* Chicago, IL 60601 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* + Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol.publishers.sfee;
import com.vasoftware.sf.soap42.webservices.ClientSoapStubFactory;
import com.vasoftware.sf.soap42.webservices.docman.DocumentFolderSoapDO;
import com.vasoftware.sf.soap42.webservices.docman.DocumentFolderSoapList;
import com.vasoftware.sf.soap42.webservices.docman.DocumentFolderSoapRow;
import com.vasoftware.sf.soap42.webservices.docman.DocumentSoapDO;
import com.vasoftware.sf.soap42.webservices.docman.DocumentSoapList;
import com.vasoftware.sf.soap42.webservices.docman.DocumentSoapRow;
import com.vasoftware.sf.soap42.webservices.docman.IDocumentAppSoap;
import com.vasoftware.sf.soap42.webservices.filestorage.IFileStorageAppSoap;
import com.vasoftware.sf.soap42.webservices.sfmain.ISourceForgeSoap;
import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.gendoc.annotations.ManualChildName;
import net.sourceforge.cruisecontrol.gendoc.annotations.SkipDoc;
import net.sourceforge.cruisecontrol.util.ValidationHelper;
import net.sourceforge.cruisecontrol.util.XPathAwareChild;
import org.jdom.Element;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import java.rmi.RemoteException;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:pj@thoughtworks.com">Paul Julius</a>
* @author <a href="mailto:krs@thoughtworks.com">Kent Spillner</a>
*/
public class SfeeDocumentManagerPublisher extends SfeePublisher {
private static final long serialVersionUID = -7627885106114057078L;
private String projectName;
private XPathAwareChild documentName;
private XPathAwareChild description;
private DataSource dataSrc;
private String path;
private Status status;
private XPathAwareChild versionComment;
private boolean lock;
private String documentPath;
@ManualChildName("status")
public XPathAwareChild createStatus() {
// This really returns a Status object, but the return type is declared as
// XPathAwareChild for documentation purpopses.
status = new Status();
return status;
}
@ManualChildName("versioncomment")
public XPathAwareChild createVersionComment() {
versionComment = new XPathAwareChild();
return versionComment;
}
public void setLock(boolean lock) {
this.lock = lock;
}
@SkipDoc
public void setData(DataSource dataSrc) {
this.dataSrc = dataSrc;
}
@ManualChildName("documentname")
public XPathAwareChild createDocumentName() {
documentName = new XPathAwareChild();
return documentName;
}
@ManualChildName("description")
public XPathAwareChild createDescription() {
description = new XPathAwareChild();
return description;
}
public void setProjectName(String name) {
projectName = name;
}
public void setFolder(String path) {
this.path = path;
}
public void publish(Element cruisecontrolLog) throws CruiseControlException {
ISourceForgeSoap sfee = (ISourceForgeSoap) ClientSoapStubFactory
.getSoapStub(ISourceForgeSoap.class, getServerURL());
String sessionID;
try {
sessionID = sfee.login(getUsername(), getPassword());
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
IFileStorageAppSoap fileStorage = (IFileStorageAppSoap) ClientSoapStubFactory
.getSoapStub(IFileStorageAppSoap.class, getServerURL());
String fileID;
String mimeType;
try {
if (documentPath != null) {
dataSrc = new FileDataSource(documentPath);
}
DataHandler dataHandler = new DataHandler(dataSrc);
fileID = fileStorage.uploadFile(sessionID, dataHandler);
mimeType = dataHandler.getContentType();
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
DocumentFolderSoapDO folder = findFolder(path);
String intendedDocumentName =
(documentName != null ? documentName.lookupValue(cruisecontrolLog) : dataSrc.getName());
String intendedVersionComment =
(versionComment != null ? versionComment.lookupValue(cruisecontrolLog) : null);
DocumentSoapDO existingDocument;
try {
existingDocument = findExistingDocument(sessionID, folder, intendedDocumentName);
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
if (existingDocument != null) {
updateExistingDocument(sessionID, existingDocument, intendedVersionComment, mimeType, fileID,
cruisecontrolLog);
} else {
createNewDocument(cruisecontrolLog, sessionID, intendedDocumentName, intendedVersionComment, folder,
mimeType, fileID);
}
}
private void updateExistingDocument(String sessionID, DocumentSoapDO existingDocument, String versionComment,
String mimeType,
String fileID, Element cruiseControlLog) throws CruiseControlException {
IDocumentAppSoap docApp = (IDocumentAppSoap) ClientSoapStubFactory
.getSoapStub(IDocumentAppSoap.class, getServerURL());
existingDocument.setDescription(description.lookupValue(cruiseControlLog));
existingDocument.setStatus(status.lookupValue(cruiseControlLog));
if (lock) {
existingDocument.setLockedBy(getUsername());
} else {
existingDocument.setLockedBy(null);
}
existingDocument.setMimeType(mimeType);
existingDocument.setVersionComment(versionComment);
try {
docApp.setDocumentData(sessionID, existingDocument, fileID);
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
}
private DocumentSoapDO findExistingDocument(String sessionID, DocumentFolderSoapDO folder,
String intendedDocumentName)
throws RemoteException {
IDocumentAppSoap docApp = (IDocumentAppSoap) ClientSoapStubFactory
.getSoapStub(IDocumentAppSoap.class, getServerURL());
DocumentSoapList documentList = docApp.getDocumentList(sessionID, folder.getId(), null);
DocumentSoapRow[] existingDocuments = documentList.getDataRows();
for (int i = 0; i < existingDocuments.length; i++) {
DocumentSoapRow existingDocument = existingDocuments[i];
if (existingDocument.getTitle().equals(intendedDocumentName)) {
return docApp
.getDocumentData(sessionID, existingDocument.getId(), existingDocument.getCurrentVersion());
}
}
return null;
}
private void createNewDocument(Element cruisecontrolLog, String sessionID, String intendedDocumentName,
String intendedVersionComment, DocumentFolderSoapDO folder, String mimeType,
String fileID) throws
CruiseControlException {
IDocumentAppSoap docApp = (IDocumentAppSoap) ClientSoapStubFactory
.getSoapStub(IDocumentAppSoap.class, getServerURL());
try {
docApp.createDocument(sessionID, folder.getId(), intendedDocumentName,
description.lookupValue(cruisecontrolLog), intendedVersionComment,
status.lookupValue(cruisecontrolLog), lock, intendedDocumentName, mimeType,
fileID);
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
}
public void subValidate() throws CruiseControlException {
ValidationHelper.assertIsSet(path, "folder", SfeeDocumentManagerPublisher.class);
ValidationHelper.assertIsSet(projectName, "projectName", SfeeDocumentManagerPublisher.class);
if (documentPath == null && dataSrc == null) {
throw new CruiseControlException("Either a document or a datasource must be specified.");
}
ValidationHelper.assertHasChild(description, "description", SfeeDocumentManagerPublisher.class);
description.validate();
ValidationHelper.assertHasChild(status, "status", SfeeDocumentManagerPublisher.class);
status.validate();
if (documentName != null) {
documentName.validate();
}
if (versionComment != null) {
versionComment.validate();
}
}
DocumentFolderSoapDO findFolder(String path) throws CruiseControlException {
if (path == null) {
throw new IllegalArgumentException("path can not be null.");
}
StringTokenizer tokens = new StringTokenizer(path, "/");
DocumentFolderSoapDO folder;
try {
ISourceForgeSoap soap = (ISourceForgeSoap) ClientSoapStubFactory
.getSoapStub(ISourceForgeSoap.class, getServerURL());
String sessionID = soap.login(getUsername(), getPassword());
String projectID = SfeeUtils.findProjectID(soap, sessionID, projectName);
IDocumentAppSoap docApp = (IDocumentAppSoap) ClientSoapStubFactory
.getSoapStub(IDocumentAppSoap.class, getServerURL());
folder = findFolderInternal(sessionID, projectID, tokens, docApp);
} catch (RemoteException e) {
throw new CruiseControlException(e);
}
if (folder == null) {
throw new CruiseControlException("Unable to find a folder for the path specified [" + path + "].");
}
return folder;
}
/**
* Recursively traverses the folder tree until the desired one is found.
*
* @return null if folder not found.
*/
private static DocumentFolderSoapDO findFolderInternal(String sessionID, String parentID,
StringTokenizer pathTokens, IDocumentAppSoap docApp)
throws RemoteException {
if (!pathTokens.hasMoreTokens()) {
return null;
}
DocumentFolderSoapList folderList = docApp.getDocumentFolderList(sessionID, parentID, false);
DocumentFolderSoapRow[] folders = folderList.getDataRows();
String nextPathElement = pathTokens.nextToken();
boolean isFinalPathElement = !pathTokens.hasMoreTokens();
for (int i = 0; i < folders.length; i++) {
DocumentFolderSoapRow nextFolder = folders[i];
String nextFolderTitle = nextFolder.getTitle();
boolean folderNamesMatch = nextFolderTitle.equals(nextPathElement);
if (folderNamesMatch && isFinalPathElement) {
return docApp.getDocumentFolderData(sessionID, nextFolder.getId());
} else if (folderNamesMatch) {
return findFolderInternal(sessionID, nextFolder.getId(), pathTokens, docApp);
}
}
return null;
}
public void setDocument(String documentPath) {
this.documentPath = documentPath;
}
public static class Status extends XPathAwareChild {
public void validate() throws CruiseControlException {
if (getXpathExpression() == null && getFixedValue() != null
&& !(getFixedValue().equalsIgnoreCase("final") || getFixedValue().equalsIgnoreCase("draft"))) {
throw new CruiseControlException(
"Status value specified [] is not valid. Valid values are 'final' and 'draft'.");
}
super.validate();
}
}
}