package com.collabnet.ce.webservices; import com.collabnet.ce.soap50.fault.NoSuchObjectFault; import com.collabnet.ce.soap50.webservices.ClientSoapStubFactory; import com.collabnet.ce.soap50.webservices.cemain.Group2SoapList; import com.collabnet.ce.soap50.webservices.cemain.Group2SoapRow; import com.collabnet.ce.soap50.webservices.cemain.ICollabNetSoap; import com.collabnet.ce.soap50.webservices.cemain.ProjectSoapRow; import com.collabnet.ce.soap50.webservices.cemain.UserSoapList; import com.collabnet.ce.soap50.webservices.cemain.UserSoapRow; import com.collabnet.ce.soap50.webservices.docman.IDocumentAppSoap; import com.collabnet.ce.soap50.webservices.filestorage.IFileStorageAppSoap; import com.collabnet.ce.soap50.webservices.frs.IFrsAppSoap; import com.collabnet.ce.soap50.webservices.rbac.IRbacAppSoap; import com.collabnet.ce.soap50.webservices.scm.IScmAppSoap; import com.collabnet.ce.soap50.webservices.tracker.ITrackerAppSoap; import hudson.plugins.collabnet.share.TeamForgeShare; import hudson.plugins.collabnet.util.CNHudsonUtil; import hudson.plugins.collabnet.util.CommonUtil; import org.apache.axis.AxisFault; import org.apache.log4j.Logger; import org.kohsuke.stapler.QueryParameter; import javax.activation.DataHandler; import javax.activation.FileDataSource; import java.io.File; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collection; import java.util.List; /*** * This class represents the connection to the CollabNet webservice. * Since it contains login/logout data, other webservices will * require an instance of it. * This is written based on the 5.0 version of the soap services. */ public class CollabNetApp { private static Logger logger = Logger.getLogger(CollabNetApp.class); public static String SOAP_SERVICE = "/ce-soap50/services/"; private String sessionId; private String username; private String url; protected final ICollabNetSoap icns; private volatile IFrsAppSoap ifrs; private volatile IFileStorageAppSoap ifsa; private volatile ITrackerAppSoap itas; private volatile IDocumentAppSoap idas; private volatile IScmAppSoap isas; private volatile IRbacAppSoap iras; /** * Creates a new session to the server at the given url. * * @param url of the CollabNet server. * @param username to login as. * @param password to login with. * @throws RemoteException if we fail to login with the username/password */ public CollabNetApp(String url, String username, String password) throws RemoteException { this(url, username); this.sessionId = this.login(password); } /** * Creates a new session to the server without actually authenticating, relying only on values passed in. * * @param url of the CollabNet server. * @param username to login as. * @param password to login with. * @param sessionId the session id */ public CollabNetApp(String url, String username, String password, String sessionId) { this(url, username); this.sessionId = sessionId; } /** * Creates a new CollabNetApp without a session. * * @param url of the CollabNet server. * @param username to login as. */ public CollabNetApp(String url, String username) { this(url); this.username = username; } /** * Creates a new collabnet app * @param url url of the CollabNet server */ public CollabNetApp(String url) { this.url = url; this.icns = this.getICollabNetSoap(); } private <T> T createProxy(Class<T> type, String wsdlLoc) { String soapURL = this.getServerUrl() + SOAP_SERVICE + wsdlLoc + "?wsdl"; return type.cast(ClientSoapStubFactory. getSoapStub(type, soapURL)); } protected ITrackerAppSoap getTrackerSoap() { if (itas==null) itas = createProxy(ITrackerAppSoap.class, "TrackerApp"); return itas; } protected IDocumentAppSoap getDocumentAppSoap() { if (idas==null) idas = createProxy(IDocumentAppSoap.class, "DocumentApp"); return idas; } protected IScmAppSoap getScmAppSoap() { if (isas==null) isas = createProxy(IScmAppSoap.class, "ScmApp"); return isas; } protected IRbacAppSoap getRbacAppSoap() { if (iras==null) iras = createProxy(IRbacAppSoap.class, "RbacApp"); return iras; } protected IFrsAppSoap getFrsAppSoap() { if (ifrs==null) ifrs = createProxy(IFrsAppSoap.class, "FrsApp"); return ifrs; } protected IFileStorageAppSoap getFileStorageAppSoap() { if (ifsa==null) ifsa = createProxy(IFileStorageAppSoap.class, "FileStorageApp"); return ifsa; } /** * Returns the user name that this connection is set up with. */ public String getUsername() { return this.username; } /** * @return the session id. */ public String getSessionId() { return this.sessionId; } /** * @return the url of the CollabNet server. */ public String getServerUrl() { return this.url; } /** * @return client soap stub for the main CollabNet.wsdl. */ private ICollabNetSoap getICollabNetSoap() { return getICollabNetSoap(url); } /** * @return client soap stub for an arbitrary url. */ private static ICollabNetSoap getICollabNetSoap(String url) { String soapURL = url + CollabNetApp.SOAP_SERVICE + "CollabNet?wsdl"; return (ICollabNetSoap) ClientSoapStubFactory. getSoapStub(ICollabNetSoap.class, soapURL); } /** * Login is only done in the constructor. If you need to * re-login, you should get a new CollabNetApp object. * * @param password used to login with. * @return a new sessionId. * @throws RemoteException */ private String login(String password) throws RemoteException { sessionId = icns.login(this.username, password); return sessionId; } /** * Login with a token. * * @param token one-time token * @return sessionId * @throws RemoteException */ public void loginWithToken(String token) throws RemoteException { this.sessionId = icns.loginWithToken(this.username, token); } /** * Logoff for this user and invalidate the sessionId. * * @throws RemoteException */ public void logoff() throws RemoteException { this.checkValidSessionId(); this.icns.logoff(this.username, this.sessionId); this.sessionId = null; } public CTFFile upload(DataHandler src) throws RemoteException { return new CTFFile(this,this.getFileStorageAppSoap().uploadFile(getSessionId(),src)); } /** * Uploads a file. The returned file object can be then used as an input * to methods like {@link CTFRelease#addFile(String, String, CTFFile)}. */ public CTFFile upload(File src) throws RemoteException { return upload(new DataHandler(new FileDataSource(src))); } /** * @param url of the CollabNet server. * @return the API version number string. This string is in the format * ${Release major}.${Release minor}.${service pack}.${hot fix} * * @throws RemoteException */ public static String getApiVersion(String url) throws RemoteException { return getICollabNetSoap(url).getApiVersion(); } /** * @return the api version number string for CTF. * * @throws RemoteException if the call fails for some unknown reason */ public String getApiVersion() throws RemoteException { return this.icns.getApiVersion(); } /** * @return the version number string for SourceForge itself. * * @throws RemoteException */ public String getVersion() throws RemoteException { this.checkValidSessionId(); return this.icns.getVersion(this.sessionId); } /** * Can the user can be found on the CollabNet server? * * @param username to check. * @return true, if the user is found, false otherwise. * @throws RemoteException */ public boolean isUsernameValid(String username) throws RemoteException { this.checkValidSessionId(); return getUser(username)!=null; } /** * Get the list of all Groups on the system. * Can only be called by SuperUsers. * * @return a Map of all group name/ids. * @throws RemoteException */ public CTFList<CTFGroup> getGroups() throws RemoteException { this.checkValidSessionId(); CTFList<CTFGroup> r = new CTFList<CTFGroup>(); Group2SoapList gsList = this.icns.getGroupList2(this.sessionId, null); for (Group2SoapRow row: gsList.getDataRows()) { r.add(new CTFGroup(this,row)); } return r; } public CTFGroup getGroupByTitle(String fullName) throws RemoteException { return getGroups().byTitle(fullName); } public CTFGroup createGroup(String fullName, String description) throws RemoteException { return new CTFGroup(this,icns.createGroup(getSessionId(),fullName,description)); } /** * Creates a new project and obtains its ID. * * @param name * ID of the project. Used as a token in URL. Can be null, in which case * inferred from the title parameter. * @param title * Human readable title of the project that can include whitespace and so on. * @param description * Longer human readable description of the project. */ public String createProject(String name, String title, String description) throws RemoteException { return this.icns.createProject(this.sessionId,name,title,description).getId(); } /** * Return a collection of users that are active members of the group. * * @param groupId * @return active users (collection of usernames). * @throws RemoteException */ public Collection<String> getGroupUsers(String groupId) throws RemoteException { this.checkValidSessionId(); Collection<String> users = new ArrayList<String>(); UserSoapList usList = this.icns.getActiveGroupMembers(this.sessionId, groupId); for (UserSoapRow row: usList.getDataRows()) { users.add(row.getUserName()); } return users; } /** * Throws a CollabNetAppException if there is no current sessionId. */ public void checkValidSessionId() { if (this.sessionId == null) { throw new CollabNetApp.CollabNetAppException("Not currently in " + "a valid session."); } } public CTFProject getProjectById(String projectId) throws RemoteException { return new CTFProject(this,icns.getProjectData(sessionId,projectId)); } public List<CTFProject> getProjects() throws RemoteException { List<CTFProject> r = new ArrayList<CTFProject>(); for (ProjectSoapRow row : icns.getProjectList(getSessionId()).getDataRows()) { r.add(new CTFProject(this,row)); } return r; } public CTFProject getProjectByTitle(String title) throws RemoteException { for (CTFProject p : getProjects()) if (p.getTitle().equals(title)) return p; return null; } /** * Returns the current user that's logged in. */ public CTFUser getMyself() throws RemoteException { return getUser(username); } /** * Retrieves the user, or null if no such user exists. */ public CTFUser getUser(String username) throws RemoteException { try { return new CTFUser(this,this.icns.getUserData(getSessionId(),username)); } catch (NoSuchObjectFault e) { return null; } catch (AxisFault e) { // somehow Axis is failing to create a strongly typed binding. if (NoSuchObjectFault.FAULT_CODE.equals(e.getFaultCode())) return null; throw e; } } /** * @param locale * Locale of the new user (currently supported locales are "en" for English, "ja" for Japanese). * @param timeZone * User's time zone. The ID for a TimeZone, either an abbreviation such as "PST", a full name such as "America/Los_Angeles", or a custom ID such as "GMT-8:00". */ public CTFUser createUser(String username, String email, String fullName, String locale, String timeZone, boolean isSuperUser, boolean isRestrictedUser, String password) throws RemoteException { return new CTFUser(this,this.icns.createUser(getSessionId(),username,email,fullName,locale,timeZone,isSuperUser,isRestrictedUser,password)); } /** * Exception class to throw when something unexpected goes wrong. */ public static class CollabNetAppException extends RuntimeException{ public CollabNetAppException(String msg) { super(msg); } } /** * A databinding method from Stapler. */ public static CollabNetApp fromStapler(@QueryParameter boolean overrideAuth, @QueryParameter String url, @QueryParameter String username, @QueryParameter String password) { TeamForgeShare.TeamForgeShareDescriptor descriptor = TeamForgeShare.getTeamForgeShareDescriptor(); if (descriptor != null && descriptor.useGlobal() && !overrideAuth) { url = descriptor.getCollabNetUrl(); username = descriptor.getUsername(); password = descriptor.getPassword(); } if (CommonUtil.unset(url) || CommonUtil.unset(username) || CommonUtil.unset(password)) { return null; } return CNHudsonUtil.getCollabNetApp(url, username, password); } }