Skip to content
Permalink
Browse files

Get closer to supporting Confluence 4.0, partially fixes JENKINS-11276

The Confluence 4.0 update includes a new SOAP endpoint (v2), and the
v1 endpoint's getPage() method now results in a remote error --
suggesting instead to use getPageSummary().  The file attachment
functionality did not rely on the wiki content being returned in
getPage(), so we now use getPageSummary() for doing file attachments.

Confluence 4.0 also introduces a new page content storage format,
which means wiki markup is no longer supported.  That means the page
editing feature of the plugin will need to be updated to work with the
v2 API's XHTML storage format.  For now, we're just fixing the file
attachment bug, and gracefully degrading on page content editing.
  • Loading branch information
Joe Hansche committed Oct 16, 2011
1 parent ef76f26 commit fda581a60920fb1ff514eae574c8273b4bdde2ac
@@ -75,9 +75,10 @@
<artifactId>axistools-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<packageSpace>hudson.plugins.confluence.soap</packageSpace>
<sourceDirectory>src/main/wsdl/</sourceDirectory>
<outputDirectory>target/generated-sources/localizer</outputDirectory>
<packageSpace>jenkins.plugins.confluence.soap</packageSpace>
<subPackageByFileName>true</subPackageByFileName>
</configuration>
<executions>
<execution>
@@ -1,6 +1,9 @@

package com.myyearbook.hudson.plugins.confluence;

import com.myyearbook.hudson.plugins.confluence.wiki.editors.MarkupEditor;
import com.myyearbook.hudson.plugins.confluence.wiki.editors.MarkupEditor.TokenNotFoundException;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
@@ -10,28 +13,21 @@
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Descriptor;
import hudson.plugins.confluence.soap.RemoteAttachment;
import hudson.plugins.confluence.soap.RemotePage;
import hudson.plugins.confluence.soap.RemotePageUpdateOptions;
import hudson.plugins.confluence.soap.RemoteSpace;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Notifier;
import hudson.tasks.Publisher;
import hudson.util.DescribableList;
import hudson.util.FormValidation;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.export.Exported;

import net.sf.json.JSONObject;

import com.myyearbook.hudson.plugins.confluence.wiki.editors.MarkupEditor;
import com.myyearbook.hudson.plugins.confluence.wiki.editors.MarkupEditor.TokenNotFoundException;

import java.io.File;
import java.io.IOException;
import java.net.URLConnection;
@@ -40,6 +36,12 @@
import java.util.List;
import java.util.logging.Logger;

import jenkins.plugins.confluence.soap.v1.RemoteAttachment;
import jenkins.plugins.confluence.soap.v1.RemotePage;
import jenkins.plugins.confluence.soap.v1.RemotePageSummary;
import jenkins.plugins.confluence.soap.v1.RemotePageUpdateOptions;
import jenkins.plugins.confluence.soap.v1.RemoteSpace;

public class ConfluencePublisher extends Notifier implements Saveable {
private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";

@@ -50,8 +52,8 @@
private final String spaceName;
private final String pageName;

private DescribableList<MarkupEditor, Descriptor<MarkupEditor>> editors =
new DescribableList<MarkupEditor, Descriptor<MarkupEditor>>(this);
private DescribableList<MarkupEditor, Descriptor<MarkupEditor>> editors = new DescribableList<MarkupEditor, Descriptor<MarkupEditor>>(
this);

@DataBoundConstructor
public ConfluencePublisher(String siteName, final String spaceName, final String pageName,
@@ -142,10 +144,10 @@ public String getSpaceName() {
}

protected boolean performAttachments(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener, ConfluenceSession confluence, RemotePage pageData)
BuildListener listener, ConfluenceSession confluence, final RemotePageSummary pageData)
throws IOException, InterruptedException {
final long pageId = pageData.getId();

final long pageId = pageData.getId();
FilePath ws = build.getWorkspace();

if (ws == null) {
@@ -251,7 +253,7 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
return true;
}

final RemotePage pageData = confluence.getPage(spaceName, pageName);
final RemotePageSummary pageData = confluence.getPageSummary(spaceName, pageName);

// Perform attachment uploads
try {
@@ -262,13 +264,20 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
e.printStackTrace(listener.getLogger());
}

// Perform wiki replacements
try {
result &= this.performWikiReplacements(build, launcher, listener, confluence, pageData);
} catch (IOException e) {
e.printStackTrace(listener.getLogger());
} catch (InterruptedException e) {
e.printStackTrace(listener.getLogger());
// Wiki editing is only supported in versions prior to 4.0
if (!confluence.isVersion4() && pageData instanceof RemotePage) {
// Perform wiki replacements
try {
result &= this.performWikiReplacements(build, launcher, listener, confluence,
(RemotePage) pageData);
} catch (IOException e) {
e.printStackTrace(listener.getLogger());
} catch (InterruptedException e) {
e.printStackTrace(listener.getLogger());
}
} else if (!editors.isEmpty()) {
log(listener, "Confluence version 4.0 has moved to a new page storage format.");
log(listener, "Not performing page edits!");
}

// Not returning `result`, because this publisher should not
@@ -316,7 +325,7 @@ protected boolean performWikiReplacements(AbstractBuild<?, ?> build, Launcher la

/**
* Recursively scan a directory, returning all files encountered
*
*
* @param artifactsDir
* @return
*/
@@ -334,7 +343,7 @@ protected boolean performWikiReplacements(AbstractBuild<?, ?> build, Launcher la

/**
* Log helper
*
*
* @param listener
* @param message
*/
@@ -395,10 +404,12 @@ public FormValidation doPageNameCheck(@QueryParameter final String siteName,

try {
ConfluenceSession confluence = site.createSession();
RemotePage page = confluence.getPage(spaceName, pageName);
RemotePageSummary page = confluence.getPageSummary(spaceName, pageName);

if (page != null) {
return FormValidation.ok("OK: " + page.getTitle());
}

return FormValidation.error("Page not found");
} catch (RemoteException re) {
return FormValidation.error(re, re.getMessage());
@@ -420,9 +431,11 @@ public FormValidation doSpaceNameCheck(@QueryParameter final String siteName,
try {
ConfluenceSession confluence = site.createSession();
RemoteSpace space = confluence.getSpace(spaceName);

if (space != null) {
return FormValidation.ok("OK: " + space.getName());
}

return FormValidation.error("Space not found");
} catch (RemoteException re) {
return FormValidation.error(re, re.getMessage());
@@ -448,8 +461,7 @@ public ConfluenceSite getSiteByName(String siteName) {
}

@Override
public boolean isApplicable(
@SuppressWarnings("rawtypes") Class<? extends AbstractProject> p) {
public boolean isApplicable(@SuppressWarnings("rawtypes") Class<? extends AbstractProject> p) {
return sites != null && sites.size() > 0;
}

@@ -2,12 +2,6 @@
package com.myyearbook.hudson.plugins.confluence;

import hudson.FilePath;
import hudson.plugins.confluence.soap.ConfluenceSoapService;
import hudson.plugins.confluence.soap.RemoteAttachment;
import hudson.plugins.confluence.soap.RemotePage;
import hudson.plugins.confluence.soap.RemotePageUpdateOptions;
import hudson.plugins.confluence.soap.RemoteServerInfo;
import hudson.plugins.confluence.soap.RemoteSpace;

import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -19,9 +13,17 @@
import java.nio.channels.WritableByteChannel;
import java.rmi.RemoteException;

import jenkins.plugins.confluence.soap.v1.ConfluenceSoapService;
import jenkins.plugins.confluence.soap.v1.RemoteAttachment;
import jenkins.plugins.confluence.soap.v1.RemotePage;
import jenkins.plugins.confluence.soap.v1.RemotePageSummary;
import jenkins.plugins.confluence.soap.v1.RemotePageUpdateOptions;
import jenkins.plugins.confluence.soap.v1.RemoteServerInfo;
import jenkins.plugins.confluence.soap.v1.RemoteSpace;

/**
* Connection to Confluence
*
*
* @author Joe Hansche <jhansche@myyearbook.com>
*/
public class ConfluenceSession {
@@ -36,30 +38,34 @@
*/
private final String token;

private final RemoteServerInfo serverInfo;

/**
* Constructor
*
*
* @param service
* @param token
*/
/* package */ConfluenceSession(final ConfluenceSoapService service, final String token) {
/* package */ConfluenceSession(final ConfluenceSoapService service, final String token,
final RemoteServerInfo info) {
this.service = service;
this.token = token;
this.serverInfo = info;
}

/**
* Get server info
*
*
* @return {@link RemoteServerInfo} instance
* @throws RemoteException
*/
public RemoteServerInfo getServerInfo() throws RemoteException {
return this.service.getServerInfo(this.token);
public RemoteServerInfo getServerInfo() {
return this.serverInfo;
}

/**
* Get a Space by key name
*
*
* @param spaceKey
* @return {@link RemoteSpace} instance
* @throws RemoteException
@@ -70,16 +76,49 @@ public RemoteSpace getSpace(String spaceKey) throws RemoteException {

/**
* Get a Page by Space and Page key names
*
*
* @param spaceKey
* @param pageKey
* @return {@link RemotePage} instance
* @throws RemoteException
* @throws UnsupportedOperationException if attempting to call this method
* against a 4.0 or newer server
* @deprecated Calling this method on a Confluence 4.0+ server will result
* in a RemoteException
*/
@Deprecated
public RemotePage getPage(String spaceKey, String pageKey) throws RemoteException {
if (isVersion4()) {
// This v1 API is broken in Confluence 4.0 and newer.
throw new UnsupportedOperationException(
"This API is not supported on Confluence version 4.0 and newer. Use getPageSummary()");
}

return this.service.getPage(this.token, spaceKey, pageKey);
}

/**
* This method is an attempt to bridge the gap between the deprecated v1
* APIs and the as-yet unimplemented v2 APIs. The v1 getPage() API no longer
* works on version 4.0+ servers, but the v2 getPage() does. The v1
* getPageSummary() is the same functionality as getPage(), minus the
* existing page content.
*
* @param spaceKey
* @param pageKey
* @return
* @throws RemoteException
*/
public RemotePageSummary getPageSummary(final String spaceKey, final String pageKey)
throws RemoteException {
if (!isVersion4()) {
// This method did not exist in the pre-4.0 v1 SOAP API
return this.getPage(spaceKey, pageKey);
}

return this.service.getPageSummary(this.token, spaceKey, pageKey);
}

public RemotePage storePage(final RemotePage page) throws RemoteException {
return this.service.storePage(this.token, page);
}
@@ -91,7 +130,7 @@ public RemotePage updatePage(final RemotePage page, final RemotePageUpdateOption

/**
* Get all attachments for a page
*
*
* @param pageId
* @return Array of {@link RemoteAttachment}s
* @throws RemoteException
@@ -102,7 +141,7 @@ public RemotePage updatePage(final RemotePage page, final RemotePageUpdateOption

/**
* Attach the file
*
*
* @param pageId
* @param fileName
* @param contentType
@@ -112,8 +151,7 @@ public RemotePage updatePage(final RemotePage page, final RemotePageUpdateOption
* @throws RemoteException
*/
public RemoteAttachment addAttachment(long pageId, String fileName, String contentType,
String comment, byte[] bytes)
throws RemoteException {
String comment, byte[] bytes) throws RemoteException {
RemoteAttachment attachment = new RemoteAttachment();
attachment.setPageId(pageId);
attachment.setFileName(sanitizeFileName(fileName));
@@ -125,7 +163,7 @@ public RemoteAttachment addAttachment(long pageId, String fileName, String conte

/**
* Attach the file
*
*
* @param pageId
* @param file
* @param contentType
@@ -135,8 +173,7 @@ public RemoteAttachment addAttachment(long pageId, String fileName, String conte
* @throws InterruptedException
*/
public RemoteAttachment addAttachment(long pageId, FilePath file, String contentType,
String comment)
throws IOException, InterruptedException {
String comment) throws IOException, InterruptedException {
ByteArrayOutputStream baos;
baos = new ByteArrayOutputStream((int) file.length());
file.copyTo(baos);
@@ -146,7 +183,7 @@ public RemoteAttachment addAttachment(long pageId, FilePath file, String content

/**
* Attach the file
*
*
* @param pageId
* @param file
* @param contentType
@@ -180,7 +217,7 @@ public RemoteAttachment addAttachment(long pageId, File file, String contentType

/**
* Sanitize the attached filename, per Confluence restrictions
*
*
* @param fileName
* @return
*/
@@ -190,4 +227,13 @@ public static String sanitizeFileName(String fileName) {
}
return hudson.Util.fixEmptyAndTrim(fileName.replace('+', '_').replace('&', '_'));
}

/**
* Returns true if this server is version 4.0 or newer.
*
* @return
*/
public boolean isVersion4() {
return this.serverInfo.getMajorVersion() >= 4;
}
}

0 comments on commit fda581a

Please sign in to comment.