1. Atlassian Tutorials
  2. Untitled project
  3. jira-rpc-plugin

Commits

Mary Anthony  committed 146a201

adding initial files

  • Participants
  • Branches master

Comments (0)

Files changed (79)

File META-INF/MANIFEST.MF

View file
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: esusatyo
+Build-Jdk: 1.6.0_31
+

File atlassian-plugin.xml

View file
+<atlassian-plugin key="com.atlassian.jira.ext.rpc" i18n-name-key="admin.web.rpc.plugin.name" name="RPC JIRA Plugin"  pluginsVersion="2" system="true">
+    <plugin-info>
+        <description key="admin.web.rpc.plugin.desc">${pom.description}</description>
+        <version>${pom.version}</version>
+        <application-version min="4.1" max="4.2"/>
+        <vendor name="${pom.organization.name}" url="${pom.organization.url}"/>
+        <bundle-instructions>
+            <Export-Package>
+                com.atlassian.jira.rpc.*
+            </Export-Package>
+        </bundle-instructions>
+    </plugin-info>
+
+    <resource type="i18n" name="i18n" location="com.atlassian.jira.rpc.i18n"/>
+
+    <component key="tokenManager" i18n-name-key="admin.web.rpc.plugin.token.manager.name"
+               name="Token Manager" class="com.atlassian.jira.rpc.auth.TokenManagerImpl" public="true">
+        <interface>com.atlassian.jira.rpc.auth.TokenManager</interface>
+    </component>
+
+    <component key="rpcProjectService" name="Project Service"
+               class="com.atlassian.jira.rpc.soap.service.ProjectServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.ProjectService</interface>
+    </component>
+
+    <component key="issueConstantsService" name="Issue Constants Service"
+               class="com.atlassian.jira.rpc.soap.service.IssueConstantsServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.IssueConstantsService</interface>
+    </component>
+
+    <component key="schemeService" name="Scheme Service"
+               class="com.atlassian.jira.rpc.soap.service.SchemeServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.SchemeService</interface>
+    </component>
+
+    <component key="rpcIssueService" name="Issue Service"
+               class="com.atlassian.jira.rpc.soap.service.IssueServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.IssueService</interface>
+    </component>
+
+    <component key="pluginSoapAttachmentHelper" name="Plugin Soap Attachment Helper"
+               class="com.atlassian.jira.rpc.soap.util.PluginSoapAttachmentHelper" public="false">
+        <interface>com.atlassian.jira.soap.axis.SoapAttachmentHelper</interface>
+   </component>
+
+    <component key="rpcSearchService" name="Search Service"
+               class="com.atlassian.jira.rpc.soap.service.SearchServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.SearchService</interface>
+    </component>
+
+    <component key="rpcUserService" name="User Service"
+               class="com.atlassian.jira.rpc.soap.service.UserServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.UserService</interface>
+    </component>
+
+    <component key="adminService" name="Admin Service"
+               class="com.atlassian.jira.rpc.soap.service.AdminServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.AdminService</interface>
+    </component>
+
+    <component key="soapService" name="SOAP Service"
+               class="com.atlassian.jira.rpc.soap.JiraSoapServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.JiraSoapService</interface>
+    </component>
+
+    <component key="soapUtilsBean" name="SOAP Utils"
+               class="com.atlassian.jira.rpc.soap.util.SoapUtilsBeanImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.util.SoapUtilsBean</interface>
+    </component>
+
+    <component key="rpcProjectRoleService" name="Project Role Service"
+               class="com.atlassian.jira.rpc.soap.service.ProjectRoleServiceImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.ProjectRoleService</interface>
+    </component>
+
+    <component key="remoteEntityFactory" name="Remote Entity Factory"
+               class="com.atlassian.jira.rpc.soap.util.RemoteEntityFactoryImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.util.RemoteEntityFactory</interface>
+    </component>
+
+    <component key="serviceHelper" name="Project and Issue service helpers"
+               class="com.atlassian.jira.rpc.soap.service.ServiceHelperImpl" public="true">
+        <interface>com.atlassian.jira.rpc.soap.service.ServiceHelper</interface>
+    </component>
+
+    <rpc-soap key="soap" name="System SOAP Services" class="com.atlassian.jira.rpc.soap.JiraSoapServiceImpl">
+        <description>The standard JIRA SOAP services.</description>
+        <service-path>jirasoapservice-v2</service-path>
+        <published-interface>com.atlassian.jira.rpc.soap.JiraSoapService</published-interface>
+    </rpc-soap>
+
+    <rpc-xmlrpc key="xmlrpc" name="System XML-RPC Services" class="com.atlassian.jira.rpc.xmlrpc.JiraXmlRpcService">
+        <description>The standard JIRA XML-RPC services.</description>
+        <service-path>jira1</service-path>
+    </rpc-xmlrpc>
+</atlassian-plugin>

File com/atlassian/jira/rpc/RpcUtils.java

View file
+package com.atlassian.jira.rpc;
+
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+public class RpcUtils
+{
+    public static Vector makeVector(Object[] objects)
+    {
+        Vector result = new Vector(objects.length);
+
+        for (int i = 0; i < objects.length; i++)
+        {
+            result.add(makeStruct(objects[i]));
+        }
+
+        return result;
+    }
+
+    public static Hashtable makeStruct(Object object)
+    {
+        try
+        {
+            // make a Hashtable, removing all null values from the Hashtable
+            // note we also don't add class as it's not a real property!
+            Hashtable result = new Hashtable(BeanUtils.describe(object))
+            {
+                public synchronized Object put(Object key, Object value)
+                {
+                    if (value == null || key == null || "class".equals(key))
+                    {
+                        return null;
+                    }
+                    return super.put(key, value);
+                }
+            };
+
+            return result;
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

File com/atlassian/jira/rpc/auth/TokenManager.java

View file
+package com.atlassian.jira.rpc.auth;
+
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.rpc.exception.RemoteAuthenticationException;
+import com.atlassian.jira.rpc.exception.RemoteException;
+import com.atlassian.jira.rpc.exception.RemotePermissionException;
+
+public interface TokenManager
+{
+    String login(String username, String password) throws RemoteException, RemoteAuthenticationException;
+
+    boolean logout(String token);
+
+    /**
+     * Retrieve the user, checking that the token is valid, and that the user has the 'USE' permission
+     *
+     * @param token
+     * @return A valid user.  Note that this method will never return null
+     * @throws RemoteAuthenticationException If the token is not valid, or if it has timed out
+     * @throws RemotePermissionException If the user does not have the 'USE' permission.
+     * @deprecated As some instances may want to allow anonymous access, individual methods should check for their
+     *             relevant permission instead.  Use {@link #retrieveUserNoPermissionCheck(String)} instead.
+     */
+    // @todo - these need to be revisited as part of JRA-7858
+    User retrieveUser(String token) throws RemoteAuthenticationException, RemotePermissionException;
+
+    // @todo - these need to be revisited as part of JRA-7858
+    User retrieveUserNoPermissionCheck(String token) throws RemoteAuthenticationException, RemotePermissionException;
+}

File com/atlassian/jira/rpc/auth/TokenManagerImpl.java

View file
+/*
+ * Copyright (c) 2002-2004
+ * All rights reserved.
+ */
+
+/*
+ */
+package com.atlassian.jira.rpc.auth;
+
+import com.atlassian.core.util.DateUtils;
+import com.atlassian.core.util.RandomGenerator;
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.bc.security.login.LoginReason;
+import com.atlassian.jira.bc.security.login.LoginResult;
+import com.atlassian.jira.bc.security.login.LoginService;
+import com.atlassian.jira.rpc.exception.RemoteAuthenticationException;
+import com.atlassian.jira.rpc.exception.RemoteException;
+import com.atlassian.jira.rpc.exception.RemotePermissionException;
+import com.atlassian.jira.security.JiraAuthenticationContext;
+import com.atlassian.jira.security.PermissionManager;
+import com.atlassian.jira.security.Permissions;
+import com.atlassian.jira.user.util.UserManager;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+
+public class TokenManagerImpl implements TokenManager
+{
+    public static long DEFAULT_TIMEOUT = 30 * DateUtils.MINUTE_MILLIS;
+    private static final Logger log = Logger.getLogger(TokenManagerImpl.class);
+
+    private final Map<String, String> userTokens;
+    private PermissionManager permissionManager;
+    private final LoginService loginService;
+    private final JiraAuthenticationContext authenticationContext;
+    private final UserManager userManager;
+    private static final String TRUSTED_APPS_TOKEN = "trustedappstoken";
+
+    public TokenManagerImpl(final PermissionManager permissionManager, final LoginService loginService, final JiraAuthenticationContext authenticationContext, final UserManager userManager)
+    {
+        this(DEFAULT_TIMEOUT, permissionManager, loginService, authenticationContext, userManager);
+    }
+
+    TokenManagerImpl(final long timeout, final PermissionManager permissionManager, final LoginService loginService, final JiraAuthenticationContext authenticationContext, final UserManager userManager)
+    {
+        this.permissionManager = permissionManager;
+        this.loginService = loginService;
+        this.authenticationContext = authenticationContext;
+        this.userManager = userManager;
+        userTokens = new TokenMap<String, String>(timeout);
+    }
+
+    public String login(String username, String password) throws RemoteException
+    {
+        //
+        // Check if a user is already authenticated say via trusted apps.  If the authentication context is non null then
+        // they must have already passed a check and we consider it kosher
+        //
+        if (authenticationContext.getLoggedInUser() != null)
+        {
+            log.debug("User '" + authenticationContext.getLoggedInUser().getName() + "' already authenticated, not attempting authentication.");
+            //
+            // we give off a special token for trusted apps but we don't require it back nor do we ever check it.
+            // we do this just to allow people to known it was trusted apps that allowed the login and any debugging will be a
+            // tad easier.
+            //
+            return TRUSTED_APPS_TOKEN;
+        }
+        User user = userManager.getUserObject(username);
+        if (user != null)
+        {
+            final LoginResult loginResult = loginService.authenticate(user, password);
+            // do they require elevated security.  if so then they cant come in via this mechanism
+            if (loginResult.getReason() == LoginReason.AUTHENTICATION_DENIED)
+            {
+                throw new RemoteAuthenticationException("Attempt to log in user '" + username + "' failed. The maximum number of failed login attempts has been reached. Please log into the application through the web interface to reset the number of failed login attempts.");
+            }
+            if (loginResult.isOK())
+            {
+                return createToken(user);
+            }
+        }
+
+        // thrown if user not found, user is null or the password didn't match.
+        throw new RemoteAuthenticationException("Invalid username or password.");
+    }
+
+    public boolean logout(String token)
+    {
+        return token == null || (userTokens.remove(token) != null);
+    }
+
+    private String createToken(User user) throws RemoteException
+    {
+        String token;
+
+
+        int count = 0;
+        token = RandomGenerator.randomString(10);
+        while (userTokens.containsKey(token) && count++ < 10)
+        {
+            token = RandomGenerator.randomString(10);
+        }
+        if (count >= 10)
+        {
+            throw new RemoteException("Error generating authentication token after 10 attempts?");
+        }
+
+        userTokens.put(token, user.getName());
+
+        return token;
+    }
+
+    public User retrieveUser(String token) throws RemoteAuthenticationException, RemotePermissionException
+    {
+        final User user = getUserFromToken(token);
+        if (!permissionManager.hasPermission(Permissions.USE, user))
+        {
+            throw new RemotePermissionException("No permission to perform operation.");
+        }
+
+        return user;
+    }
+
+    public User retrieveUserNoPermissionCheck(String token)
+            throws RemoteAuthenticationException, RemotePermissionException
+    {
+        if (StringUtils.isBlank(token))
+        {
+            return null;
+        }
+        return getUserFromToken(token);
+
+    }
+
+    private User getUserFromToken(final String token) throws RemoteAuthenticationException
+    {
+        //
+        // if we have someone in the authentication context then that trumps any tokens as they must
+        // have come in via trusted apps or the like
+        //
+        User user = authenticationContext.getLoggedInUser();
+        if (user != null)
+        {
+            log.debug("Ignoring token '" + token + "' because user '" + user.getName() + "' is already in the AuthenticationContext.");
+            return user;
+        }
+        // Retrieve user from token
+        String userName = userTokens.get(token);
+        if (StringUtils.isNotBlank(userName))
+        {
+            user = userManager.getUserObject(userName);
+        }
+        if (user == null)
+        {
+            throw new RemoteAuthenticationException("User not authenticated yet, or session timed out.");
+        }
+        return user;
+    }
+}

File com/atlassian/jira/rpc/auth/TokenMap.java

View file
+/*
+ * Copyright (c) 2002-2004
+ * All rights reserved.
+ */
+
+/*
+ */
+package com.atlassian.jira.rpc.auth;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A simple map with a timeout on the get/put methods.
+ */
+public class TokenMap<K, V> extends HashMap<K, V>
+{
+    private final long tokenTimeout;
+    private final Map<Object, Long> tokenTimeouts;
+    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
+    private final Lock r = rwLock.readLock();
+    private final Lock w = rwLock.writeLock();
+
+    public TokenMap(long tokenTimeout)
+    {
+        this.tokenTimeout = tokenTimeout;
+        this.tokenTimeouts = new HashMap<Object, Long>();
+    }
+
+    public V put(final K key, final V value)
+    {
+        w.lock();
+        try
+        {
+            tokenTimeouts.put(key, nextExpiryTime());
+            return super.put(key, value);
+        }
+        finally
+        {
+            w.unlock();
+        }
+    }
+
+    private Long nextExpiryTime()
+    {
+        return System.currentTimeMillis() + tokenTimeout;
+    }
+
+    public V get(Object key)
+    {
+        r.lock();
+        try
+        {
+            if (!super.containsKey(key))
+            {
+                return null;
+            }
+
+            Long expiryTime = tokenTimeouts.get(key);
+            if (expiryTime == null)
+            {
+                tokenTimeouts.remove(key);
+                super.remove(key);
+                return null;
+            }
+            else if (expiryTime < System.currentTimeMillis()) // expired!
+            {
+                tokenTimeouts.remove(key);
+                super.remove(key);
+                return null;
+            }
+            else // we're still timed in, extend another timeout
+            {
+                tokenTimeouts.put(key, nextExpiryTime());
+            }
+
+            return super.get(key);
+        }
+        finally
+        {
+            r.unlock();
+        }
+    }
+
+    public boolean containsKey(final Object key)
+    {
+        r.lock();
+        try
+        {
+            return super.containsKey(key);
+        }
+        finally
+        {
+            r.unlock();
+        }
+    }
+
+    public V remove(Object key)
+    {
+        w.lock();
+        try
+        {
+            tokenTimeouts.remove(key);
+            return super.remove(key);
+        }
+        finally
+        {
+            w.unlock();
+        }
+    }
+
+    public void clear()
+    {
+        w.lock();
+        try
+        {
+            tokenTimeouts.clear();
+            super.clear();
+        }
+        finally
+        {
+            w.unlock();
+        }
+    }
+}

File com/atlassian/jira/rpc/exception/RemoteAuthenticationException.java

View file
+/*
+ * Created by IntelliJ IDEA.
+ * User: Mike
+ * Date: Aug 19, 2004
+ * Time: 9:25:05 AM
+ */
+package com.atlassian.jira.rpc.exception;
+
+
+/**
+ * An exception thrown for remote authentication failures or errors.
+ */
+public class RemoteAuthenticationException extends RemoteException
+{
+    ///CLOVER:OFF
+    public RemoteAuthenticationException()
+    {
+    }
+
+    public RemoteAuthenticationException(String s)
+    {
+        super(s);
+    }
+
+    public RemoteAuthenticationException(String s, Throwable throwable)
+    {
+        super(s, throwable);
+    }
+
+    public RemoteAuthenticationException(Throwable throwable)
+    {
+        super(throwable);
+    }
+}

File com/atlassian/jira/rpc/exception/RemoteException.java

View file
+/*
+* Created by IntelliJ IDEA.
+* User: Mike
+* Date: Aug 19, 2004
+* Time: 9:21:31 AM
+*/
+package com.atlassian.jira.rpc.exception;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+
+
+/**
+ * A general exception that occurs remotely.
+ */
+public class RemoteException extends Exception
+{
+    ///CLOVER:OFF
+    public RemoteException()
+    {
+    }
+
+    public RemoteException(String s)
+    {
+        super(s);
+    }
+
+    public RemoteException(String s, Throwable throwable)
+    {
+        super(s + "Caused by " + ExceptionUtils.getStackTrace(throwable));
+    }
+
+    public RemoteException(Throwable throwable)
+    {
+        super(ExceptionUtils.getStackTrace(throwable));
+    }
+}

File com/atlassian/jira/rpc/exception/RemotePermissionException.java

View file
+/*
+ * Created by IntelliJ IDEA.
+ * User: Mike
+ * Date: Aug 19, 2004
+ * Time: 9:21:50 AM
+ */
+package com.atlassian.jira.rpc.exception;
+
+
+/**
+ * Exception thrown when permissions are violated remotely.
+ */
+public class RemotePermissionException extends RemoteException
+{
+    ///CLOVER:OFF
+    public RemotePermissionException()
+    {
+    }
+
+    public RemotePermissionException(String s)
+    {
+        super(s);
+    }
+
+    public RemotePermissionException(String s, Throwable throwable)
+    {
+        super(s, throwable);
+    }
+
+    public RemotePermissionException(Throwable throwable)
+    {
+        super(throwable);
+    }
+}

File com/atlassian/jira/rpc/exception/RemoteValidationException.java

View file
+/*
+ * Created by IntelliJ IDEA.
+ * User: Mike
+ * Date: Aug 19, 2004
+ * Time: 9:41:57 AM
+ */
+package com.atlassian.jira.rpc.exception;
+
+import com.atlassian.jira.util.ErrorCollection;
+
+import java.util.Iterator;
+
+/**
+ * Exception thrown when remote data does not validate properly.
+ */
+public class RemoteValidationException extends RemoteException
+{
+    ///CLOVER:OFF
+    public RemoteValidationException()
+    {
+    }
+
+    public RemoteValidationException(String s)
+    {
+        super(s);
+    }
+
+    public RemoteValidationException(String s, Throwable throwable)
+    {
+        super(s, throwable);
+    }
+
+    public RemoteValidationException(Throwable throwable)
+    {
+        super(throwable);
+    }
+
+    public RemoteValidationException(String s, ErrorCollection errorCol)
+    {
+        super(makeNiceMessage(s, errorCol));
+    }
+
+    private static String makeNiceMessage(String s, ErrorCollection errorCol)
+    {
+        StringBuffer message = new StringBuffer(s);
+        message.append(" ");
+
+
+        if (errorCol.getErrors() != null)
+        {
+            for (Iterator iterator = errorCol.getErrors().keySet().iterator(); iterator.hasNext();)
+            {
+                String field = (String) iterator.next();
+                message.append(field).append(":").append(errorCol.getErrors().get(field)).append(" ");
+            }
+        }
+
+        if (errorCol.getErrorMessages() != null)
+        {
+            for (Iterator iterator = errorCol.getErrorMessages().iterator(); iterator.hasNext();)
+            {
+                String errorMessage = (String) iterator.next();
+                message.append(errorMessage).append(" ");
+            }
+        }
+        return message.toString();
+    }
+}

File com/atlassian/jira/rpc/i18n.properties

View file
+admin.web.rpc.plugin.name=RPC JIRA Plugin
+admin.web.rpc.plugin.desc=The standard JIRA RPC services, both SOAP and XML-RPC.

File com/atlassian/jira/rpc/soap/JiraSoapService.java

View file
+package com.atlassian.jira.rpc.soap;
+
+import com.atlassian.jira.rpc.exception.RemoteAuthenticationException;
+import com.atlassian.jira.rpc.exception.RemoteException;
+import com.atlassian.jira.rpc.exception.RemotePermissionException;
+import com.atlassian.jira.rpc.exception.RemoteValidationException;
+import com.atlassian.jira.rpc.soap.beans.RemoteAttachment;
+import com.atlassian.jira.rpc.soap.beans.RemoteAvatar;
+import com.atlassian.jira.rpc.soap.beans.RemoteComment;
+import com.atlassian.jira.rpc.soap.beans.RemoteComponent;
+import com.atlassian.jira.rpc.soap.beans.RemoteConfiguration;
+import com.atlassian.jira.rpc.soap.beans.RemoteEntity;
+import com.atlassian.jira.rpc.soap.beans.RemoteField;
+import com.atlassian.jira.rpc.soap.beans.RemoteFieldValue;
+import com.atlassian.jira.rpc.soap.beans.RemoteFilter;
+import com.atlassian.jira.rpc.soap.beans.RemoteGroup;
+import com.atlassian.jira.rpc.soap.beans.RemoteIssue;
+import com.atlassian.jira.rpc.soap.beans.RemoteIssueType;
+import com.atlassian.jira.rpc.soap.beans.RemoteNamedObject;
+import com.atlassian.jira.rpc.soap.beans.RemotePermission;
+import com.atlassian.jira.rpc.soap.beans.RemotePermissionScheme;
+import com.atlassian.jira.rpc.soap.beans.RemotePriority;
+import com.atlassian.jira.rpc.soap.beans.RemoteProject;
+import com.atlassian.jira.rpc.soap.beans.RemoteProjectRole;
+import com.atlassian.jira.rpc.soap.beans.RemoteProjectRoleActors;
+import com.atlassian.jira.rpc.soap.beans.RemoteResolution;
+import com.atlassian.jira.rpc.soap.beans.RemoteRoleActors;
+import com.atlassian.jira.rpc.soap.beans.RemoteScheme;
+import com.atlassian.jira.rpc.soap.beans.RemoteSecurityLevel;
+import com.atlassian.jira.rpc.soap.beans.RemoteServerInfo;
+import com.atlassian.jira.rpc.soap.beans.RemoteStatus;
+import com.atlassian.jira.rpc.soap.beans.RemoteUser;
+import com.atlassian.jira.rpc.soap.beans.RemoteVersion;
+import com.atlassian.jira.rpc.soap.beans.RemoteWorklog;
+
+import java.util.Date;
+
+/**
+ * This interface represents the methods that can be invoked remotely via SOAP.
+ * <p/>
+ * In general they take a security token that is given out via the {JiraSoapService#login}
+ * method to ensure that the user making a method call has the right permissions to make that call.
+ * <p/>
+ * However there is an exception to this rule.  If your HTTP request has been authenticated via another mechanism,
+ * such as trusted application links, OAUTH or Basic Auth, then the token parameter can be ignored since the
+ * user has already been validated and set in place.
+ */
+public interface JiraSoapService
+{
+    /**
+     * This will authenticate the user in JIRA and returned a authentication token that can then be used on all other
+     * SOAP methods.
+     *
+     * @param username the JIRA user name to authenticate
+     * @param password the password of the JIRA user
+     * @return a authentication token that can then be used on further SOAP calls.
+     * @throws RemoteException               If there was some problem preventing the operation from working.
+     * @throws RemoteAuthenticationException If the username and password is an invalid combination
+     */
+    String login(String username, String password) throws RemoteException, RemoteAuthenticationException;
+
+    /**
+     * Cleans up an authentication token that was previously created with a call to {@link JiraSoapService#login(String, String)}
+     *
+     * @param token the SOAP authentication token
+     * @return true if the logout succeeded
+     */
+    boolean logout(String token);
+
+    /**
+     * Returns information about the server JIRA is running on including build number and base URL.
+     *
+     * @param token the SOAP authentication token.
+     * @return a {@link RemoteServerInfo} object
+     */
+    RemoteServerInfo getServerInfo(String token);
+
+    /**
+     * Returns the Project with the matching id (if the user has permission to browse it).
+     *
+     * @param token     the SOAP authentication token.
+     * @param projectId the id of the requested project
+     * @return the RemoteProject object specified by the key, if it exists and the user has the BROWSE permission for
+     *         it
+     * @throws RemotePermissionException     if the User does not have permission to BROWSE the project.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteProject getProjectById(String token, Long projectId)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns the Project with the matching id (if the user has permission to browse it) with notification, issue
+     * security and permission schemes attached.
+     *
+     * @param token     the SOAP authentication token.
+     * @param projectId the id of the requested project
+     * @return the RemoteProject object specified by the key, if it exists and the user has the BROWSE permission for
+     *         it
+     * @throws RemotePermissionException     if the User does not have permission to BROWSE the project.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if something dramatic happens during this operation
+     */
+    RemoteProject getProjectWithSchemesById(String token, Long projectId) throws RemoteException;
+
+    /**
+     * Returns the Project with the matching key (if the user has permission to browse it).
+     *
+     * @param token      the SOAP authentication token.
+     * @param projectKey the key of the requested project
+     * @return the RemoteProject object specified by the key, if it exists and the user has the BROWSE permission for
+     *         it
+     * @throws RemotePermissionException     if the User does not have permission to BROWSE the project.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if something dramatic happens during this operation
+     */
+    RemoteProject getProjectByKey(String token, String projectKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns an array of all the versions for the specified project key.
+     *
+     * @param token      the SOAP authentication token.
+     * @param projectKey the key of the requested project
+     * @return an array of {@link RemoteVersion} objects
+     * @throws RemoteException               If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteVersion[] getVersions(String token, String projectKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns an array of all the components for the specified project key.
+     *
+     * @param token      the SOAP authentication token.
+     * @param projectKey the key of the requested project
+     * @return an array of {@link RemoteComponent} objects
+     * @throws RemoteException               If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteComponent[] getComponents(String token, String projectKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns an array of all the (non-sub task) issue types for the specified project id.
+     *
+     * @param token     the SOAP authentication token.
+     * @param projectId id of the project
+     * @return an array of {@link RemoteIssueType} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteIssueType[] getIssueTypesForProject(String token, String projectId)
+            throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the sub task issue types for the specified project id.
+     *
+     * @param token     the SOAP authentication token.
+     * @param projectId id of the project
+     * @return an array of {@link RemoteIssueType} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteIssueType[] getSubTaskIssueTypesForProject(String token, String projectId)
+            throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the issue types for all projects in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link RemoteIssueType} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteIssueType[] getIssueTypes(String token) throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the sub task issue types in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link RemoteIssueType} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteIssueType[] getSubTaskIssueTypes(String token)
+            throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the issue priorities in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link RemotePriority} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemotePriority[] getPriorities(String token) throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the issue statuses in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link RemoteStatus} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteStatus[] getStatuses(String token) throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns an array of all the issue resolutions in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link RemoteResolution} objects
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteResolution[] getResolutions(String token) throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Returns information about a user defined to JIRA.
+     *
+     * @param token    the SOAP authentication token.
+     * @param username the user name to look up
+     * @return a {@link RemoteUser} or null if it cant be found
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteUser getUser(String token, String username) throws RemotePermissionException, RemoteAuthenticationException;
+
+    /**
+     * Creates a user in JIRA with the specified user details.
+     *
+     * @param token    the SOAP authentication token.
+     * @param username the user name to create
+     * @param password the password for the new user
+     * @param fullName the full name of the new user
+     * @param email    the email of the new user
+     * @return the newly created {@link RemoteUser}
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteUser createUser(String token, String username, String password, String fullName, String email)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteValidationException, RemoteException;
+
+    /**
+     * Deletes a user in JIRA with the specified username.
+     *
+     * @param token    the SOAP authentication token.
+     * @param username the user name to delete
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the arguments and their properties are incomplete or malformed.
+     */
+    void deleteUser(String token, String username)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Updates the details about a user
+     *
+     * @param token the SOAP authentication token.
+     * @param user  the ruser details to update
+     * @return the new represensentation of the user
+     * @throws RemotePermissionException     If the ruser is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteUser updateUser(String token, RemoteUser user)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteValidationException, RemoteException;
+
+
+    /**
+     * Sets the password for a specified user
+     *
+     * @param token       the SOAP authentication token.
+     * @param user        the ruser to update
+     * @param newPassword the new password for that user
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the arguments and their properties are incomplete or malformed.
+     */
+    void setUserPassword(String token, RemoteUser user, String newPassword)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteValidationException, RemoteException;
+
+    /**
+     * Find the group with the specified name in JIRA.
+     *
+     * @param token     the SOAP authentication token.
+     * @param groupName the name of the group to find.
+     * @return a {@link RemoteGroup} object for the found group or null if it cant be found.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteGroup getGroup(String token, String groupName)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Creates a group with the given name optionally adding the given user to it.
+     *
+     * @param token     the SOAP authentication token.
+     * @param groupName the name of the group to create.
+     * @param firstUser the user to add to the group (if null, no user will be added).
+     * @return the group.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the group name, such as if the group name
+     *                                       exists.
+     */
+    RemoteGroup createGroup(String token, String groupName, RemoteUser firstUser)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Adds the specified user to the specified group
+     *
+     * @param token the SOAP authentication token.
+     * @param group the name of the group to add the user to.
+     * @param ruser the name of the user to add
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the operation.
+     */
+    void addUserToGroup(String token, RemoteGroup group, RemoteUser ruser)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Removes the specified user to the specified group
+     *
+     * @param token the SOAP authentication token.
+     * @param group the name of the group to remove the user from.
+     * @param ruser the name of the user to remove
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the operation.
+     */
+    void removeUserFromGroup(String token, RemoteGroup group, RemoteUser ruser)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Updates the details of a group
+     *
+     * @param token the SOAP authentication token.
+     * @param group the to be updated group
+     * @return the new roup information
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the operation.
+     */
+    RemoteGroup updateGroup(String token, RemoteGroup group)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Deletes the specified group by name
+     *
+     * @param token         the SOAP authentication token.
+     * @param groupName     the name of the group to delete.
+     * @param swapGroupName identifies the group to change comment and worklog visibility to.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the operation.
+     */
+    void deleteGroup(String token, String groupName, String swapGroupName)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This retrieves a list of the currently logged in user's favourite fitlers.
+     *
+     * @param token the SOAP authentication token.
+     * @return a list of the currently logged in user's favourite fitlers.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the group name, such as if the group name
+     *                                       exists.
+     * @deprecated since v3.13.  Please use {@link #getFavouriteFilters(String)}
+     */
+    RemoteFilter[] getSavedFilters(String token)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * This retreives a list of the currently logged in user's favourite fitlers.
+     *
+     * @param token the SOAP authentication token.
+     * @return a list of the currently logged in user's favourite fitlers.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If there is a problem with the group name, such as if the group name
+     *                                       exists.
+     */
+    RemoteFilter[] getFavouriteFilters(String token)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns information about the specific issue as identified by the issue key
+     *
+     * @param token    the SOAP authentication token.
+     * @param issueKey the key of the issue to return
+     * @return a representation of the issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue getIssue(String token, String issueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns information about the specific issue as identified by the issue id
+     *
+     * @param token   the SOAP authentication token.
+     * @param issueId the id of the issue to return
+     * @return a representation of the issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue getIssueById(String token, String issueId)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Given an issue key, this method returns the resolution date for this issue.  If the issue hasn't been resolved
+     * yet, this method will return null.
+     * <p/>
+     * If the no issue with the given key exists a RemoteException will be thrown.
+     *
+     * @param token    the SOAP authentication token
+     * @param issueKey the key of the issue
+     * @return The resolution date of the issue. May be null
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @since 4.0
+     */
+    Date getResolutionDateByKey(String token, String issueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Given an issue id, this method returns the resolution date for this issue.  If the issue hasn't been resolved
+     * yet, this method will return null.
+     * <p/>
+     * If the no issue with the given id exists a RemoteException will be thrown.
+     *
+     * @param token   the SOAP authentication token
+     * @param issueId the id of the issue
+     * @return The resolution date of the issue. May be null
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @since 4.0
+     */
+    Date getResolutionDateById(String token, Long issueId)
+            throws RemotePermissionException, RemoteAuthenticationException,
+            RemoteException;
+
+    /**
+     * Gets a specific comment by comment id
+     *
+     * @param token the SOAP authentication token
+     * @param id    the id of the comment to retrieve
+     * @return the comment with the specified id
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if the comment does not exist or your dont have permission to see it
+     */
+    RemoteComment getComment(String token, Long id) throws RemoteException;
+
+    /**
+     * Gets the comments for an issue
+     *
+     * @param token    the SOAP authentication token
+     * @param issueKey the key of the issue to get comments for
+     * @return the comment with the specified id
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               if the issue does not exist or your dont have permission to see it
+     */
+    RemoteComment[] getComments(String token, String issueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * This will create an issue based on the passed in details.
+     *
+     * If you are updating Select or Multi-Select custom fields, note that you should be passing in the Option Ids,
+     * as opposed to the actual Option values. If you pass in the Option values, we will attempt to do an automatic
+     * conversion from Option value to an Option Id if we find that none of the values passed in can be parsed to an
+     * Option Id for the field. 
+     *
+     * In the case where an Option has a value that is also a valid Option Id for the field, and the supplied value
+     * matches this Option value, we assume the supplied value is an OptionId.
+     *
+     * If any automatic conversion fails, the original values are passed through and cause an exception with the valid
+     * Option Ids for the field to be thrown. 
+     *
+     * @param token  the SOAP authentication token
+     * @param rIssue the new issue details to create
+     * @return the newly created issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue createIssue(String token, RemoteIssue rIssue)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This will create an issue based on the passed in details and use the security level
+     *
+     * See also {@link #createIssue(String, com.atlassian.jira.rpc.soap.beans.RemoteIssue)}
+     *
+     * @param token           the SOAP authentication token
+     * @param rIssue          the new issue details to create
+     * @param securityLevelId the id of the required security level
+     * @return the newly created issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue createIssueWithSecurityLevel(String token, RemoteIssue rIssue, Long securityLevelId)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This will create an issue based on the passed in details and make it a child (eg subtask) of the specified parent issue.
+     *
+     * See also {@link #createIssue(String, com.atlassian.jira.rpc.soap.beans.RemoteIssue)}
+     *
+     * @param token  the SOAP authentication token
+     * @param rIssue the new issue details to create
+     * @param parentIssueKey the key of the parent issue
+     * @return the newly created issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue createIssueWithParent(String token, RemoteIssue rIssue, String parentIssueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This will create an issue based on the passed in details and use the security level and make it a child (eg subtask) of the specified parent issue.
+     *
+     * See also {@link #createIssue(String, com.atlassian.jira.rpc.soap.beans.RemoteIssue)}
+     *
+     * @param token           the SOAP authentication token
+     * @param rIssue          the new issue details to create
+     * @param parentIssueKey the key of the parent issue
+     * @param securityLevelId the id of the required security level
+     * @return the newly created issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    RemoteIssue createIssueWithParentWithSecurityLevel(String token, RemoteIssue rIssue, String parentIssueKey, Long securityLevelId)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Add attachments to an issue.
+     * <p/>
+     * This method accepts the data of the attachments as byte arrays. This is known to cause problems when the
+     * attachments are above a certain size. For more information, please see <a href="http://jira.atlassian.com/browse/JRA-11693">JRA-11693</a>.
+     *
+     * @param token       the SOAP authentication token.
+     * @param issueKey    the issue to attach to
+     * @param fileNames   an array of filenames; each element names an attachment to be uploaded
+     * @param attachments an array of byte arrays; each element contains the data of the attachment to be uploaded
+     * @return true if attachments were successfully added; if the operation was not successful, an exception would be
+     *         thrown
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @deprecated since v4.0 please use {@link #addBase64EncodedAttachmentsToIssue(String, String, String[],
+     *             String[])}
+     */
+    boolean addAttachmentsToIssue(String token, String issueKey, String[] fileNames, byte[][] attachments)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * An alternative mechanism for adding attachments to an issue. This method accepts the data of the attachments as
+     * Base64 encoded strings instead of byte arrays. This is to combat the XML message bloat created by Axis when
+     * SOAP-ifying byte arrays.
+     * <p/>
+     * For more information, please see <a href="http://jira.atlassian.com/browse/JRA-11693">JRA-11693</a>.
+     *
+     * @param token                       the SOAP authentication token.
+     * @param issueKey                    the issue to attach to
+     * @param fileNames                   an array of filenames; each element names an attachment to be uploaded
+     * @param base64EncodedAttachmentData an array of Base 64 encoded Strings; each element contains the data of the
+     *                                    attachment to be uploaded
+     * @return true if attachments were successfully added; if the operation was not successful, an exception would be
+     *         thrown
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @see #addAttachmentsToIssue(String, String, String[], byte[][])
+     */
+    boolean addBase64EncodedAttachmentsToIssue(String token, String issueKey, String[] fileNames, String[] base64EncodedAttachmentData)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Returns the attachments that are associated with an issue
+     *
+     * @param token    the SOAP authentication token.
+     * @param issueKey the issue to find attachments for
+     * @return the attachments of the issue
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    RemoteAttachment[] getAttachmentsFromIssue(String token, String issueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This will delete an issue with the specified issue key
+     *
+     * @param token    the SOAP authentication token
+     * @param issueKey the issue to delete
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    void deleteIssue(String token, String issueKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Adds a comment to the specified issue
+     *
+     * @param token         the SOAP authentication token
+     * @param issueKey      the key of the issue to add a comment to
+     * @param remoteComment the new comment to add
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               If the issue does not exist or your dont have permission to see it
+     */
+    void addComment(String token, String issueKey, RemoteComment remoteComment)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns true if the user has permission to edit a comment
+     *
+     * @param token         the SOAP authentication token
+     * @param remoteComment the comment to edit
+     * @return true if the user has permission to make this edit
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    boolean hasPermissionToEditComment(String token, RemoteComment remoteComment) throws RemoteException;
+
+    /**
+     * Allows a comment to be edited
+     *
+     * @param token         the SOAP authentication token
+     * @param remoteComment the new comment details
+     * @return the updated comment details
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    RemoteComment editComment(String token, RemoteComment remoteComment) throws RemoteException;
+
+    /**
+     * This will update an issue with new values.
+     * <p/>
+     * NOTE : You cannot update the 'status' field of the issue via this method.  You must instead call :
+     * <p/>
+     * {@link #progressWorkflowAction(String, String, String, com.atlassian.jira.rpc.soap.beans.RemoteFieldValue[])}
+     * <p/>
+     * to progress the issue status into a new workflow state.
+     *
+     * If you are updating Select or Multi-Select custom fields, note that you should be passing in the Option Ids,
+     * as opposed to the actual Option values. If you pass in the Option values, we will attempt to do an automatic
+     * conversion from Option value to an Option Id if we find that none of the values passed in can be parsed to an
+     * Option Id for the field. 
+     *
+     * In the case where an Option has a value that is also a valid Option Id for the field, and the supplied value
+     * matches this Option value, we assume the supplied value is an OptionId.
+     *
+     * If any automatic conversion fails, the original values are passed through and cause an exception with the valid
+     * Option Ids for the field to be thrown. 
+     *
+     * @param token        the SOAP authentication token.
+     * @param issueKey     the issue to update.
+     * @param actionParams the list of issue fields to change
+     * @return the updated RemoteIssue
+     * @throws RemoteException if the issue cannot be updated
+     */
+    RemoteIssue updateIssue(String token, String issueKey, RemoteFieldValue[] actionParams) throws RemoteException;
+
+    /**
+     * This will progress an issue through a workflow.
+     *
+     * @param token          the SOAP authentication token.
+     * @param issueKey       the issue to update.
+     * @param actionIdString the workflow action to progress to
+     * @param actionParams   the list of issue fields to change in this workflow step
+     * @return the updated RemoteIssue
+     * @throws RemoteException if the issue cannot be updated
+     */
+    RemoteIssue progressWorkflowAction(String token, String issueKey, String actionIdString, RemoteFieldValue[] actionParams)
+            throws RemoteException;
+
+    /**
+     * Returns the fields that are shown during an issue creation.
+     *
+     * @param token    the SOAP authentication token.
+     * @param projectKey the project to create the issue in.
+     * @param issueTypeId the type of issue to create
+     * @return the fields that would be shown during the create operation
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    RemoteField[] getFieldsForCreate(String token, String projectKey, Long issueTypeId) throws RemoteException;
+
+    /**
+     * Returns the fields that are shown during an issue edit.
+     *
+     * @param token    the SOAP authentication token.
+     * @param issueKey the issue to update.
+     * @return the fields that would be shown during the edit operation
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    RemoteField[] getFieldsForEdit(String token, String issueKey) throws RemoteException;
+
+    /**
+     * Returns the available actions that can be applied to an issue.
+     *
+     * @param token    the SOAP authentication token.
+     * @param issueKey the issue to get actions for.
+     * @return the available actions that can be applied to an issue
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    RemoteNamedObject[] getAvailableActions(String token, String issueKey) throws RemoteException;
+
+    /**
+     * Returns the fields that are shown during an issue action.
+     *
+     * @param token          the SOAP authentication token.
+     * @param issueKey       the issue to update.
+     * @param actionIdString the id of issue action to be executed
+     * @return the fields that would be shown during the issue action operation
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    RemoteField[] getFieldsForAction(String token, String issueKey, String actionIdString) throws RemoteException;
+
+    /**
+     * Creates a project based on the passed in details.
+     *
+     * @param token               the SOAP authentication token
+     * @param key                 the new project key
+     * @param name                the new project name
+     * @param description         the description of the new project
+     * @param url                 the new project URL
+     * @param lead                the user who is trhe project lead
+     * @param permissionScheme    the permission to use for the new project
+     * @param notificationScheme  the notification scheme to use on the new project
+     * @param issueSecurityScheme the issue security scheme to use on the new project
+     * @return the newly created project
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If something dramatic happened during the execution of the operation
+     */
+    RemoteProject createProject(String token, String key, String name, String description, String url, String lead, RemotePermissionScheme permissionScheme, RemoteScheme notificationScheme, RemoteScheme issueSecurityScheme)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Creates a project based on the passed in details.
+     *
+     * @param token    the SOAP authentication token
+     * @param rproject the object representation of the new project
+     * @return the newly created project
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If something dramatic happened during the execution of the operation
+     */
+    RemoteProject createProjectFromObject(String token, RemoteProject rproject)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * Updates a project based on the passed in details.
+     *
+     * @param token    the SOAP authentication token
+     * @param rProject the object representation of the updated project
+     * @return the updated project
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteValidationException     If the data cannot be validated
+     * @throws RemoteException               If something dramatic happened during the execution of the operation
+     */
+    RemoteProject updateProject(String token, RemoteProject rProject)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException, RemoteValidationException;
+
+    /**
+     * This will delete a project with the specified project key
+     *
+     * @param token      the SOAP authentication token
+     * @param projectKey the project to delete
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     * @throws RemoteException               If something dramatic happened during the execution of the operation
+     */
+    void deleteProject(String token, String projectKey)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns all the custom fields available
+     *
+     * @param token the SOAP authentication token.
+     * @return all the custom fields available
+     * @throws RemoteException If something dramatic happened during the execution of the operation
+     */
+    RemoteField[] getCustomFields(String token) throws RemoteException;
+
+    /**
+     * Addsa a new version to the specified project.
+     *
+     * @param token         the SOAP authentication token.
+     * @param projectKey    the key of the project to add the new version to
+     * @param remoteVersion the new version details
+     * @return the newly added version
+     * @throws RemoteException If something dramatic happened during the execution of the operation
+     */
+    RemoteVersion addVersion(String token, String projectKey, RemoteVersion remoteVersion) throws RemoteException;
+
+    /**
+     * Refreshes the internal representation of all the custom fields available
+     *
+     * @param token the SOAP authentication token.
+     * @throws RemoteException If something dramatic happened during the execution of the operation
+     */
+    void refreshCustomFields(String token) throws RemoteException;
+
+    // Search methods
+
+    /**
+     * Returns issues that match the saved filter specified by the filterId.
+     * <p/>
+     * This method will return no more than the maxNumResults.
+     * <p/>
+     * It will start the result set at the provided off set.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token    the SOAP authentication token.
+     * @param filterId identifies the saved filter to use for the search.
+     * @return issues matching the saved filter
+     * @throws RemoteException If there was some problem preventing the operation from working.
+     * @deprecated use {@link #getIssuesFromFilterWithLimit(String, String, int, int)} instead
+     */
+    RemoteIssue[] getIssuesFromFilter(String token, String filterId) throws RemoteException;
+
+    /**
+     * Returns issues containing searchTerms.
+     * <p/>
+     * Note: this is a fuzzy search, returned in order of 'relevance', so the results are only generally useful for
+     * human consumption.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token       the SOAP authentication token.
+     * @param searchTerms search terms
+     * @return issues matching the search terms
+     * @throws RemoteException If there was some problem preventing the operation from working.
+     * @see #getIssuesFromJqlSearch(String, String, int)
+     * @deprecated use {@link #getIssuesFromTextSearchWithLimit(String, String, int, int)}  instead
+     */
+    RemoteIssue[] getIssuesFromTextSearch(String token, String searchTerms) throws RemoteException;
+
+    /**
+     * Returns issues that match the saved filter specified by the filterId.
+     * <p/>
+     * This method will return no more than the maxNumResults.
+     * <p/>
+     * It will start the result set at the provided off set.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token         the SOAP authentication token.
+     * @param filterId      identifies the saved filter to use for the search.
+     * @param offSet        the place in the result set to use as the first result returned
+     * @param maxNumResults the maximum number of results that this method will return.
+     * @return issues matching the saved filter
+     * @throws RemoteException If there was some problem preventing the operation from working.
+     */
+    RemoteIssue[] getIssuesFromFilterWithLimit(String token, String filterId, int offSet, int maxNumResults)
+            throws RemoteException;
+
+    /**
+     * Returns issues containing searchTerms.
+     * <p/>
+     * Note: this is a fuzzy search, returned in order of 'relevance', so the results are only generally useful for
+     * human consumption.
+     * <p/>
+     * This method will return no more than the maxNumResults.
+     * <p/>
+     * It will start the result set at the provided off set.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token         the SOAP authentication token.
+     * @param searchTerms   search terms
+     * @param offSet        the place in the result set to use as the first result returned
+     * @param maxNumResults the maximum number of results that this method will return.
+     * @return issues matching the search terms
+     * @throws RemoteException If there was some problem preventing the operation from working.
+     * @see #getIssuesFromJqlSearch(String, String, int)
+     */
+    RemoteIssue[] getIssuesFromTextSearchWithLimit(String token, String searchTerms, int offSet, int maxNumResults)
+            throws RemoteException;
+
+    /**
+     * Returns issues containing searchTerms that are within the specified projects.
+     * <p/>
+     * Note: this is a fuzzy search, returned in order of 'relevance', so the results are only generally useful for
+     * human consumption.
+     * <p/>
+     * This method will return no more than the maxNumResults.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token         the SOAP authentication token.
+     * @param projectKeys   an array of project keys to search within
+     * @param searchTerms   search terms
+     * @param maxNumResults the maximum number of results that this method will return.
+     * @return issues matching the search terms
+     * @throws RemoteException If there was some problem preventing the operation from working.
+     * @see #getIssuesFromJqlSearch(String, String, int)
+     */
+    RemoteIssue[] getIssuesFromTextSearchWithProject(String token, String[] projectKeys, String searchTerms, int maxNumResults)
+            throws RemoteException;
+
+    /**
+     * Execute a specified JQL query and return the resulting issues.
+     * <p/>
+     * This method also respects the jira.search.views.max.limit and jira.search.views.max.unlimited.group JIRA
+     * properties which will override the max number of results returned.
+     * <p/>
+     * If the jira.search.views.max.limit property is set and you are not in a group specified by
+     * jira.search.views.max.unlimited.group then the number of results returned will be constrained by the value of
+     * jira.search.views.max.limit if it is less than the specified maxNumResults.
+     *
+     * @param token         the SOAP authentication token
+     * @param jqlSearch     JQL query string to execute
+     * @param maxNumResults the maximum number of results that this method will return
+     * @return issues matching the JQL query
+     * @throws RemoteException If there was a JQL parse error or an error occurs during the search
+     */
+    RemoteIssue[] getIssuesFromJqlSearch(String token, String jqlSearch, int maxNumResults) throws RemoteException;
+
+    /**
+     * Returns an array of all the Projects defined in JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return an array of {@link com.atlassian.jira.rpc.soap.beans.RemoteProject} objects.
+     * @throws RemoteException               If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteProject[] getProjectsNoSchemes(String token)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Returns the count of issues that would be returned for a given filter
+     *
+     * @param token    the SOAP authentication token.
+     * @param filterId the id of the search filter
+     * @return the count of matching issues
+     * @throws RemoteException if something dramatic happens during this operation
+     */
+    long getIssueCountForFilter(String token, String filterId) throws RemoteException;
+
+
+    /**
+     * Returns information about the current configuration of JIRA.
+     *
+     * @param token the SOAP authentication token.
+     * @return a {@link com.atlassian.jira.rpc.soap.beans.RemoteConfiguration} object which contains information about
+     *         the current configuration of JIRA.
+     * @throws RemoteException               If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException     If the user is not permitted to perform this operation in this context.
+     * @throws RemoteAuthenticationException If the token is invalid or the SOAP session has timed out
+     */
+    RemoteConfiguration getConfiguration(String token)
+            throws RemotePermissionException, RemoteAuthenticationException, RemoteException;
+
+    /**
+     * Adds a worklog to the given issue and sets the issue's remaining estimate field to the given value. The issue's
+     * time spent field will be increased by the amount in the remoteWorklog.getTimeSpent().
+     * <p/>
+     * The following fields of remoteWorklog are ignored: <ul> <li>id: generated by the system.</li> <li>author: derived
+     * from the authenticated user.</li> <li>updatedAuthor: derived from the authenticated user.</li> <li>created:
+     * derived from the current date.</li> <li>updated: derived from the current date.</li> </ul>
+     *
+     * @param token                the SOAP authentication token.
+     * @param issueKey             the key of the issue.
+     * @param remoteWorklog        the worklog to add.
+     * @param newRemainingEstimate the new value for the issue's remaining estimate as a duration string, eg 1d 2h.
+     * @return Created worklog with the id set or null if no worklog was created.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteWorklog addWorklogWithNewRemainingEstimate(String token, String issueKey, RemoteWorklog remoteWorklog, String newRemainingEstimate)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Adds a worklog to the given issue. The issue's time spent field will be increased by the amount in the
+     * remoteWorklog.getTimeSpent().
+     * <p/>
+     * The following fields of remoteWorklog are ignored: <ul> <li>id: generated by the system.</li> <li>author: derived
+     * from the authenticated user.</li> <li>updatedAuthor: derived from the authenticated user.</li> <li>created:
+     * derived from the current date.</li> <li>updated: derived from the current date.</li> </ul>
+     *
+     * @param token         the SOAP authentication token.
+     * @param issueKey      the key of the issue.
+     * @param remoteWorklog the worklog to add.
+     * @return Created worklog with the id set or null if no worklog was created.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteWorklog addWorklogAndAutoAdjustRemainingEstimate(String token, String issueKey, RemoteWorklog remoteWorklog)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Adds a worklog to the given issue but leaves the issue's remaining estimate field unchanged. The issue's time
+     * spent field will be increased by the amount in the remoteWorklog.getTimeSpent().
+     * <p/>
+     * The following fields of remoteWorklog are ignored: <ul> <li>id: generated by the system.</li> <li>author: derived
+     * from the authenticated user.</li> <li>updatedAuthor: derived from the authenticated user.</li> <li>created:
+     * derived from the current date.</li> <li>updated: derived from the current date.</li> </ul>
+     *
+     * @param token         the SOAP authentication token.
+     * @param issueKey      the key of the issue.
+     * @param remoteWorklog the worklog to add.
+     * @return Created worklog with the id set or null if no worklog was created.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteWorklog addWorklogAndRetainRemainingEstimate(String token, String issueKey, RemoteWorklog remoteWorklog)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Returns all worklogs for the given issue.
+     *
+     * @param token    the SOAP authentication token.
+     * @param issueKey the key of the issue.
+     * @return all the worklogs of the issue.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    RemoteWorklog[] getWorklogs(String token, String issueKey)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Deletes the worklog with the given id and sets the remaining estimate field on the isssue to the given value. The
+     * time spent field of the issue is reduced by the time spent amount on the worklog being deleted.
+     *
+     * @param token                the SOAP authentication token.
+     * @param worklogId            the id of the worklog to delete.
+     * @param newRemainingEstimate the new value for the issue's remaining estimate as a duration string, eg 1d 2h.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    void deleteWorklogWithNewRemainingEstimate(String token, String worklogId, String newRemainingEstimate)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Deletes the worklog with the given id and updates the remaining estimate field on the isssue by increasing it by
+     * the time spent amount on the worklog being deleted. The time spent field of the issue is reduced by the time
+     * spent amount on the worklog being deleted.
+     *
+     * @param token     the SOAP authentication token.
+     * @param worklogId the id of the worklog to delete.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    void deleteWorklogAndAutoAdjustRemainingEstimate(String token, String worklogId)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**
+     * Deletes the worklog with the given id but leaves the remaining estimate field on the isssue unchanged. The time
+     * spent field of the issue is reduced by the time spent amount on the worklog being deleted.
+     *
+     * @param token     the SOAP authentication token.
+     * @param worklogId the id of the worklog to delete.
+     * @throws RemoteException           If there was some problem preventing the operation from working.
+     * @throws RemotePermissionException If the user is not permitted to perform this operation in this context.
+     * @throws RemoteValidationException If the arguments and their properties are incomplete or malformed.
+     */
+    void deleteWorklogAndRetainRemainingEstimate(String token, String worklogId)
+            throws RemoteException, RemotePermissionException, RemoteValidationException;
+
+    /**