Java tutorial
/* * Copyright 2009 CollabNet, Inc. ("CollabNet") Licensed under the Apache * License, Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.collabnet.tracker.core; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.net.UnknownHostException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.rpc.ServiceException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.axis.AxisFault; import org.apache.axis.EngineConfiguration; import org.apache.axis.message.MessageElement; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.collabnet.ccf.pi.cee.pt.v50.ProjectTrackerHelper; import com.collabnet.ccf.pi.cee.pt.v50.ProjectTrackerReader; import com.collabnet.core.ws.exception.WSException; import com.collabnet.core.ws.services.Dispatcher; import com.collabnet.core.ws.services.DispatcherService; import com.collabnet.core.ws.services.DispatcherServiceLocator; import com.collabnet.core.ws.services.ProjectInfo; import com.collabnet.core.ws.services.Request; import com.collabnet.core.ws.services.Response; import com.collabnet.core.ws.services.SystemStatus; import com.collabnet.core.ws.services.SystemStatusService; import com.collabnet.core.ws.services.SystemStatusServiceLocator; import com.collabnet.core.ws.services.Version; import com.collabnet.tracker.common.ClientArtifact; import com.collabnet.tracker.common.ClientArtifactComment; import com.collabnet.tracker.common.ClientArtifactListXMLHelper; import com.collabnet.tracker.common.WebServiceClient; import com.collabnet.tracker.core.model.TrackerArtifactType; import com.collabnet.tracker.core.model.TrackerClientData; import com.collabnet.tracker.core.util.TrackerUtil; import com.collabnet.tracker.ws.ArtifactHistoryList; import com.collabnet.tracker.ws.ArtifactType; import com.collabnet.tracker.ws.ArtifactTypeMetadata; import com.collabnet.tracker.ws.HistoryTransactionList; import com.collabnet.tracker.ws.Metadata; import com.collabnet.tracker.ws.MetadataService; import com.collabnet.tracker.ws.MetadataServiceLocator; import com.collabnet.tracker.ws.Query; import com.collabnet.tracker.ws.attachment.AttachmentManager; import com.collabnet.tracker.ws.attachment.AttachmentService; import com.collabnet.tracker.ws.attachment.AttachmentServiceLocator; import com.collabnet.tracker.ws.history.ArtifactHistoryManager; import com.collabnet.tracker.ws.history.ArtifactHistoryService; import com.collabnet.tracker.ws.history.ArtifactHistoryServiceLocator; import com.collabnet.tracker.ws.query.QueryManager; import com.collabnet.tracker.ws.query.QueryManagerService; import com.collabnet.tracker.ws.query.QueryManagerServiceLocator; /** * This is the interface to tracker through Axis. This class handles all xml * requests and results through axis. * * @author Shawn Minto * */ public class TrackerWebServicesClient { private static final Log log = LogFactory.getLog(TrackerWebServicesClient.class); public static final String ISSUE_URL = "/servlets/Scarab?id="; public static final String NEW_ISSUE_URL = "/servlets/Scarab/action/CreateArtifact"; public static final String DEFINED_QUERY_URL = "/servlets/Scarab/action/ExecuteQuery?query="; public static final String EDIT_QUERY_URL = "/servlets/Scarab/remcurreport/true/template/EditQuery"; public static final String ATTACHMENT_URL = "/servlets/ScarabDownload/remcurreport/true/template/ViewAttachment.vm/attachid/**ID**/filename/**FILENAME**"; public static final String DEFAULT_NAMESPACE = "urn:ws.tracker.collabnet.com"; public static final String HISTORY_URL_1 = "/servlets/Scarab/template/ViewIssue.vm/id/"; public static final String HISTORY_URL_2 = "/eventsubmit_dosetissueview/foo/action/ViewIssue/tab/5/"; public static final String API_VERSION = "1.2.0"; public static final String TEXT_TAG = "tag.type.text"; public static final String VALUE_TAG = "tag.type.value"; public static final String CHANGE_OPERATION = "change"; public static final String LEAVE_OPERATION = "leave"; public static final String REASON_FOR_CHANGE_TYPE = "_REASON_FOR_CHANGE"; public static final String MODIFIED_BY_FIELD_NAME = "modifiedBy"; public static final String LAST_READ_ON_FIELD_NAME = "lastReadOn"; private WebServiceClient mClient; private TrackerClientData repositoryData; private Proxy proxy; private String httpUser; private String httpPassword; public TrackerWebServicesClient(String url, String username, String password, Proxy proxy, String httpUser, String httpPassword) throws MalformedURLException { mClient = new WebServiceClient(); mClient.init(username, password, url); this.proxy = proxy; this.httpUser = httpUser; this.httpPassword = httpPassword; } /** * This method is used by the TrackerRepositoryConfigurationPage to validate * the settings * * @throws Exception */ public void checkConnection() throws Exception { try { EngineConfiguration config = mClient.getEngineConfiguration(); SystemStatusService service = new SystemStatusServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/SystemStatus"); SystemStatus theService = service.getSystemStatusService(portAddress); Version serverVer = theService.getVersion(); if (isVersionGreaterOrEqual(serverVer, "1.2.0")) { ProjectInfo projInfo = theService.getProjectInfo(getProjectNameFromUrl()); if (!projInfo.isPtEnabled()) { throw new TrackerException("This project does not support project tracker."); } } // see if user has access to pt Collection<TrackerArtifactType> artifactTypes = getArtifactTypes(); if (artifactTypes.isEmpty()) { throw new TrackerException("You do not have access to any artifact types in this project"); } validateApiVersion(theService.getVersion()); } catch (AxisFault af) { if (af.getMessage().toLowerCase().contains("no permission for web services - execute")) { throw new TrackerException( "Could not connect to server, ensure WS Execute permissions are set (contact site administrator)."); } else if (af.getMessage().toLowerCase().contains("no password")) { throw new TrackerException( "Could not connect to server, ensure username and password are correct."); } else if (af.getCause() instanceof UnknownHostException) { throw new TrackerException("Could not connect to server, ensure server url is correct."); } else if (af.getMessage().contains("CoreWSException")) { throw new TrackerException("Could not connect to server, ensure server url is correct."); } else if (af.getMessage().contains("Error in getProjectInfo")) { throw new TrackerException("This is not a valid project."); } else { throw new Exception(af.getMessage()); } } } public ClientArtifactListXMLHelper createArtifactList(ClientArtifact artifact, boolean provideReason, boolean isFoundReasonForCurrentlyProcessedArtifact, String reasonForCurrentlyProcessedArtifact) throws Exception { this.sanitizeComments(artifact); Document doc = null; doc = createNewXMLDocument(DEFAULT_NAMESPACE, "ns1:" + "createArtifactList"); ArrayList<ClientArtifact> caList = new ArrayList<ClientArtifact>(); caList.add(artifact); ClientArtifactListXMLHelper helper = this.createOrUpdateArtifactList(doc, caList, null, provideReason, isFoundReasonForCurrentlyProcessedArtifact, reasonForCurrentlyProcessedArtifact); ProjectTrackerHelper ptHelper = ProjectTrackerHelper.getInstance(); ptHelper.processWSErrors(helper); List<ClientArtifactComment> comments = artifact.getComments(); if (comments != null && comments.size() > 0) { List<ClientArtifact> responseArtifacts = helper.getAllArtifacts(); ClientArtifact responseArtifact = responseArtifacts.get(0); String createdArtifactId = responseArtifact.getAttributeValue(ProjectTrackerReader.TRACKER_NAMESPACE, "id"); artifact.addAttributeValue(ProjectTrackerReader.TRACKER_NAMESPACE, "id", createdArtifactId); String modifiedOn = responseArtifact.getAttributeValue(ProjectTrackerReader.TRACKER_NAMESPACE, ProjectTrackerReader.MODIFIED_ON_FIELD); long modifiedOnMilliSeconds = Long.parseLong(modifiedOn); helper = this.updateArtifactList(caList, modifiedOnMilliSeconds, provideReason, isFoundReasonForCurrentlyProcessedArtifact, reasonForCurrentlyProcessedArtifact); } return helper; } /** * Get all of the server defined queries for the project * * @return * @param artifact * @throws Exception * @throws Exception * * * NOTE: All artifact in the List must belong to the same * namespace and artifactType * */ public ClientArtifactListXMLHelper createArtifactList(List<ClientArtifact> artifacts, boolean provideReason, boolean isFoundReasonForCurrentlyProcessedArtifact, String reasonForCurrentlyProcessedArtifact) throws Exception { Document doc = null; doc = createNewXMLDocument(DEFAULT_NAMESPACE, "ns1:" + "createArtifactList"); ClientArtifactListXMLHelper helper = this.createOrUpdateArtifactList(doc, artifacts, null, provideReason, isFoundReasonForCurrentlyProcessedArtifact, reasonForCurrentlyProcessedArtifact); return helper; } public ClientArtifactListXMLHelper createOrUpdateArtifactList(Document doc, List<ClientArtifact> artifacts, Long lastReadOn, boolean provideReason, boolean isFoundReasonForCurrentlyProcessedArtifact, String reasonForCurrentlyProcessedArtifact) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); Element root = doc.getDocumentElement(); Element artifactListNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + "artifactList"); root.appendChild(artifactListNode); // TODO: Move all the below code to clientArtifact.java? HashMap<String, Integer> nameSpaces = new HashMap<String, Integer>(); // List<String> nameSpaces = new ArrayList<String>(); int nameSpaceCount = 1; nameSpaces.put(DEFAULT_NAMESPACE, nameSpaceCount); for (int i = 0; i < artifacts.size(); i++) { ClientArtifact ca = artifacts.get(i); String nsXNameSpace = ca.getNamespace(); String artifactType = ca.getTagName(); // int nsCtr; // check if the namespace alrady exists in the xml so far if (nameSpaces.get(nsXNameSpace) == null) { nameSpaces.put(nsXNameSpace, ++nameSpaceCount); } String nsNumberString = "ns" + nameSpaces.get(nsXNameSpace) + ":"; Element artifactNode = doc.createElementNS(nsXNameSpace, nsNumberString + artifactType); artifactListNode.appendChild(artifactNode); Element modByNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + MODIFIED_BY_FIELD_NAME); modByNode.appendChild(doc.createTextNode(mClient.getUserName())); artifactNode.appendChild(modByNode); if (lastReadOn != null) { Element lastReadNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + LAST_READ_ON_FIELD_NAME); lastReadNode.appendChild(doc.createTextNode(Long.toString(lastReadOn))); artifactNode.appendChild(lastReadNode); } else { Element lastReadNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + LAST_READ_ON_FIELD_NAME); lastReadNode.appendChild(doc.createTextNode(Long.toString(new Date().getTime()))); artifactNode.appendChild(lastReadNode); } // Add each attribute Map<String, List<String>> textAttributes = ca.getAttributes(); for (Map.Entry<String, List<String>> attributeEntry : textAttributes.entrySet()) { String attribute = attributeEntry.getKey(); List<String> values = attributeEntry.getValue(); // strip the namespace from the attribute key String[] parts = attribute.substring(1).split("\\}"); String attributeNamespace = parts[0]; attribute = parts[1]; if (nameSpaces.get(attributeNamespace) == null) { nameSpaces.put(attributeNamespace, ++nameSpaceCount); } nsNumberString = "ns" + nameSpaces.get(attributeNamespace) + ":"; Element attributeNode = doc.createElementNS(attributeNamespace, nsNumberString + attribute); if (values.size() > 1 || (attributeNamespace.equals(DEFAULT_NAMESPACE) && attribute.equals("id"))) { for (String value : values) { if (!(StringUtils.isEmpty(value))) { Element valueNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:value"); // valueNode.setNodeValue(value); // we do not need this line any more because the // GenericArtifactXMLHelper will care // about it // value = // TrackerUtil.removeInvalidXmlCharacters(value); valueNode.appendChild(doc.createTextNode(value)); attributeNode.appendChild(valueNode); } } } else { // TODO: consider the namespace of the attributes? // attributeNode.setNodeValue(values.get(0)); String value = values.get(0); value = TrackerUtil.removeInvalidXmlCharacters(value); if (value == null) value = ""; attributeNode.appendChild(doc.createTextNode(value)); } artifactNode.appendChild(attributeNode); } List<ClientArtifactComment> comments = ca.getComments(); for (ClientArtifactComment comment : comments) { String commentText = comment.getCommentText(); Element commentNode = doc.createElementNS("urn:ws.tracker.collabnet.com", "ns1:" + "comment"); Element textNode = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + "text"); commentText = TrackerUtil.removeInvalidXmlCharacters(commentText); textNode.appendChild(doc.createTextNode(commentText)); commentNode.appendChild(textNode); artifactNode.appendChild(commentNode); } if (provideReason || isFoundReasonForCurrentlyProcessedArtifact) { Element reasonNode = doc.createElementNS("urn:ws.tracker.collabnet.com", "ns1:" + "reason"); reasonNode.appendChild(doc.createTextNode( provideReason ? "Synchronized by Connector" : reasonForCurrentlyProcessedArtifact)); artifactNode.appendChild(reasonNode); } } // for every artifact Element sendMail = doc.createElementNS(DEFAULT_NAMESPACE, "ns1:" + "sendEmail"); sendMail.appendChild(doc.createTextNode("true")); root.appendChild(sendMail); if (log.isDebugEnabled()) this.printDocument(doc); Response r = theService.execute(toRequest(doc)); Document result = toDocument(r); if (log.isDebugEnabled()) this.printDocument(result); ClientArtifactListXMLHelper helper = new ClientArtifactListXMLHelper(result); return helper; } /** * Download an attachment from PT * * @param taskId * @param attachmentId * @return * @throws ServiceException * @throws NumberFormatException * @throws IOException */ public InputStream downloadAttachmentAsStream(String taskId, String attachmentId) throws ServiceException, NumberFormatException, IOException { EngineConfiguration config = mClient.getEngineConfiguration(); AttachmentService service = new AttachmentServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Attachment"); AttachmentManager theService = service.getAttachmentService(portAddress); DataHandler handler = theService.getAttachment(taskId, Long.parseLong(attachmentId)); return handler.getInputStream(); // // byte[] attachment = null; // InputStream inputStream = null; // ByteArrayOutputStream baos = null; // try { // inputStream = handler.getInputStream(); // baos = new ByteArrayOutputStream(2048); // byte[] buffer = new byte[2048]; // int n = 0; // while (-1 != (n = inputStream.read(buffer))) { // baos.write(buffer, 0, n); // } // // attachment = baos.toByteArray(); // baos.close(); // inputStream.close(); // return attachment; // } finally { // if (baos != null) // baos.close(); // if (inputStream != null) // inputStream.close(); // } } /** * Execute the predefined server query and return the results * * @param query * @return * @throws Exception */ public ClientArtifactListXMLHelper executePredefinedQuery(Query query) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); String xmlName = query.getTagName(); String namespace = query.getNamespace(); String runQuery = "<getArtifactList xmlns='urn:ws.tracker.collabnet.com'> " + " <namedQuery>" + " <tagName>" + xmlName + "</tagName>" + " <namespace>" + namespace + "</namespace>" + "</namedQuery></getArtifactList>"; TrackerUtil.debug("executePredefinedQuery(): " + xmlName); Response r = theService.execute(toRequest(createDocument(runQuery))); Document result = toDocument(r); ClientArtifactListXMLHelper helper = new ClientArtifactListXMLHelper(result); return helper; } public List<ClientArtifact> getAllArtifacts(String artifactType, String nameSpace) throws Exception { if (nameSpace == null) nameSpace = mClient.getDefaultNamespace(); EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); String runQuery = "<getArtifactList xmlns='urn:ws.tracker.collabnet.com'><adhocQuery><artifactTypes><artifactType><tagName>" + artifactType + "</tagName><namespace>" + nameSpace + "</namespace></artifactType></artifactTypes></adhocQuery></getArtifactList>"; Request queryRequest = toRequest(createDocument(runQuery)); Response queryResponse = theService.execute(queryRequest); Document queryResponseDocument = toDocument(queryResponse); ClientArtifactListXMLHelper result = new ClientArtifactListXMLHelper(queryResponseDocument); return result.getAllArtifacts(); } /** * Get all of the server defined queries for the project * * @return * @throws ServiceException * @throws WSException * @throws RemoteException */ public Query[] getAllProjectDefinedQueries() throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); QueryManagerService queryService = new QueryManagerServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Query"); QueryManager queryManager = queryService.getQueryManagerService(portAddress); Query[] queries = queryManager.getProjectQueries(); return queries; } /** * Get a single artifact given its id * * @param taskId * @return * @throws Exception */ public ClientArtifactListXMLHelper getArtifactById(String taskId) throws Exception { Set<String> idList = new HashSet<String>(1); idList.add(taskId); return getArtifactsById(idList); } /** * Get all artifact changes between from and to times * * @param ata * @param from * @param to * @return * @throws Exception */ public HistoryTransactionList getArtifactChanges(ArtifactType[] ata, long from, Long to) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); ArtifactHistoryService service = new ArtifactHistoryServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/ArtifactHistory"); ArtifactHistoryManager theService = service.getArtifactHistoryManager(portAddress); /* * Long to = new Long(System.currentTimeMillis()); long from = * to.longValue(); try { from = Long.parseLong(lastSynchDateTime); } * catch(NumberFormatException nfe) { } */ TrackerUtil.debug("request artifactChanges() from: " + new Date(from)); return theService.getArtifactChanges(ata, from, to); } public HistoryTransactionList getArtifactChanges(Set<String> kinds, String lastSynchDateTime) throws Exception { ArtifactType[] items = new ArtifactType[kinds.size()]; int i = 0; for (String artitfactNamespaceAndType : kinds) { String[] parts = artitfactNamespaceAndType.substring(1).split("\\}"); String artifactType = parts[1]; String namespace = parts[0]; items[i] = new ArtifactType(); items[i].setNamespace(namespace); items[i].setTagName(artifactType); i++; } long from = 0; try { from = Long.parseLong(lastSynchDateTime); } catch (NumberFormatException nfe) { log.warn("From time is invalid " + lastSynchDateTime + ". Resorting to 0"); } return this.getArtifactChanges(items, from, null); } /** * Get all of the artifacts given a list of ids * * @param idList * @return * @throws Exception */ public ClientArtifactListXMLHelper getArtifactsById(Set<String> idList) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); String runQuery = "<getArtifactById xmlns='urn:ws.tracker.collabnet.com'> "; for (String id : idList) { runQuery += "<id>" + id + "</id>"; } runQuery += "</getArtifactById>"; TrackerUtil.debug("getArtifactsById(): "); Response r = theService.execute(toRequest(createDocument(runQuery))); Document result = toDocument(r); ClientArtifactListXMLHelper helper = new ClientArtifactListXMLHelper(result); return helper; } /** * Queries the server and returns a list of artifact types. * * @return * @throws ServiceException * @throws WSException * @throws RemoteException */ public Collection<TrackerArtifactType> getArtifactTypes() throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); MetadataService service = new MetadataServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Metadata"); Metadata theService = service.getMetadataService(portAddress); ArtifactType[] artifactTypes = theService.getArtifactTypes(); String key; for (ArtifactType type : artifactTypes) { key = TrackerUtil.getKey(type.getNamespace(), type.getTagName()); if (repositoryData == null) repositoryData = new TrackerClientData(); if (repositoryData.getArtifactTypeFromKey(key) == null) repositoryData.addArtifactType(new TrackerArtifactType(type)); } return repositoryData.getArtifactTypes(); } /** * Get changed artifacts since last synch time * * @param kinds * @param lastSynchDateTime * @return * @throws Exception */ public String[] getChangedArtifacts(Set<String> kinds, String lastSynchDateTime) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); ArtifactHistoryService service = new ArtifactHistoryServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/ArtifactHistory"); ArtifactHistoryManager theService = service.getArtifactHistoryManager(portAddress); ArtifactType[] items = new ArtifactType[kinds.size()]; int i = 0; for (String artitfactNamespaceAndType : kinds) { String[] parts = artitfactNamespaceAndType.substring(1).split("\\}"); String artifactType = parts[1]; String namespace = parts[0]; items[i] = new ArtifactType(); items[i].setNamespace(namespace); items[i].setTagName(artifactType); i++; } long from = 0; try { from = Long.parseLong(lastSynchDateTime); } catch (NumberFormatException nfe) { log.warn("From time is invalid " + lastSynchDateTime + ". Resorting to 0"); } return theService.getChangedArtifactIDs(items, from, null); } /** * This queries PT to get the list of changed artifacts in range * * @param artitfactNamespaceAndType * @param minTaskId * @param maxTaskId * @param dateString * @return * @throws Exception */ public List<String> getChangedIds(String artitfactNamespaceAndType, String minTaskId, String maxTaskId, String dateString) throws Exception { EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); String[] parts = artitfactNamespaceAndType.substring(1).split("\\}"); String artifactType = parts[1]; String namespace = parts[0]; minTaskId = minTaskId.toLowerCase(); maxTaskId = maxTaskId.toLowerCase(); String lastSyncDate = dateString; String now = new Date().getTime() + ""; String runQuery = "<getArtifactList xmlns='urn:ws.tracker.collabnet.com'> " + "<adhocQuery>" + "<artifactTypes>" + "<artifactType>" + "<tagName>" + artifactType + "</tagName>" + "<namespace>" + namespace + "</namespace>" + "</artifactType>" + "</artifactTypes>" + "<idRange>" + "<min>" + minTaskId + "</min>" + "<max>" + maxTaskId + "</max>" + "</idRange>" + "<modifiedOn>" + "<rangeCondition>" + "<min>" + lastSyncDate + "</min>" + "<max>" + now + "</max>" + "</rangeCondition>" + "</modifiedOn>" + "</adhocQuery>" + "</getArtifactList>"; TrackerUtil.debug("request isChanged() for min: " + minTaskId + " max: " + maxTaskId); Response r = theService.execute(toRequest(createDocument(runQuery))); Document result = toDocument(r); ClientArtifactListXMLHelper helper = new ClientArtifactListXMLHelper(result); if (helper.getErrorSize() > 0) { // if we have an error, we will make it update so that we can try to // sync return new ArrayList<String>(0); } return helper.getAllArtifactIds(); } public ArtifactHistoryList getChangeHistoryForArtifact(String artifactId, long from, Long to) throws Exception { String[] artifactList = new String[1]; artifactList[0] = artifactId; return this.getChangeHistoryForArtifact(artifactList, from, to); } public ArtifactHistoryList getChangeHistoryForArtifact(String[] artifactList, long from, Long to) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); ArtifactHistoryService service = new ArtifactHistoryServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/ArtifactHistory"); ArtifactHistoryManager theService = service.getArtifactHistoryManager(portAddress); return theService.getArtifactHistory(artifactList, from, to); } public DataHandler getDataHandlerForAttachment(String taskId, String attachmentId) throws ServiceException, WSException, NumberFormatException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); AttachmentService service = new AttachmentServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Attachment"); AttachmentManager theService = service.getAttachmentService(portAddress); DataHandler handler = theService.getAttachment(taskId, Long.parseLong(attachmentId)); return handler; } public String getHttpPassword() { return httpPassword; } public String getHttpUser() { return httpUser; } /** * Get the metadata for the given artifact. The metadata contains the valid * values for attributes and the valid operations that can be performed on * the attribute * * @param namespace * @param artifactType * @param artifactId * @return * @throws ServiceException * @throws WSException * @throws RemoteException */ public ArtifactTypeMetadata getMetaDataForArtifact(String namespace, String artifactType, String artifactId) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); MetadataService service = new MetadataServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Metadata"); Metadata theService = service.getMetadataService(portAddress); TrackerUtil.debug("getMetaDataForArtifact():" + artifactId); ArtifactTypeMetadata metaData = theService .getMetadataForArtifact(new ArtifactType(artifactType, namespace, ""), artifactId); TrackerUtil.debug("getMetaDataForArtifact():done "); TrackerArtifactType type = repositoryData .getArtifactTypeFromKey(TrackerUtil.getKey(namespace, artifactType)); if (type == null) { type = new TrackerArtifactType(metaData.getArtifactType().getDisplayName(), metaData.getArtifactType().getTagName(), metaData.getArtifactType().getNamespace()); } type.populateAttributes(metaData); repositoryData.addArtifactType(type); return metaData; } public ArtifactTypeMetadata getMetaDataForArtifactType(String namespace, String artifactType) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); MetadataService service = new MetadataServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Metadata"); Metadata theService = service.getMetadataService(portAddress); return theService.getArtifactTypeMetadata(new ArtifactType(artifactType, namespace, "")); } /** * Get the metadata that can be used when creating a new PT artifact. This * should be used when artifact creation is supported * * @param namespace * @param artifactType * @return * @throws ServiceException * @throws WSException * @throws RemoteException */ public ArtifactTypeMetadata getMetaDataForNewArtifact(String namespace, String artifactType) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); MetadataService service = new MetadataServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Metadata"); Metadata theService = service.getMetadataService(portAddress); return theService.getMetadataForNewArtifact(new ArtifactType(artifactType, namespace, "")); } /** * Get the next page of results. This is used by the * ClientArtifactListXMLHelper to ensure that all results are returned * * @param pageInfo * @return * @throws Exception */ public Document getNextPage(Node pageInfo, String altQueryRef) throws Exception { validateNextPage(pageInfo, altQueryRef); EngineConfiguration config = mClient.getEngineConfiguration(); DispatcherService service = new DispatcherServiceLocator(config); URL portAddress = mClient.constructServiceURL("/ws/Dispatcher"); Dispatcher theService = service.getDispatcher(portAddress); Document doc = createNewXMLDocument(DEFAULT_NAMESPACE, "ns1:" + "getNextPage"); Element root = doc.getDocumentElement(); Node newPageInfo = doc.importNode(pageInfo, true); root.appendChild(newPageInfo); TrackerUtil.debug("getNextPage()"); Response r = theService.execute(toRequest(doc)); Document result = toDocument(r); return result; } /** * Parses the project name from the repository url. * * @return */ public String getProjectNameFromUrl() { String aUrl = mClient.getURL(); if (aUrl == null) return ""; int prefixIndex = aUrl.indexOf("//"); int postfixIndex = aUrl.indexOf("."); prefixIndex = prefixIndex == -1 ? 0 : prefixIndex + 2; postfixIndex = postfixIndex == -1 ? aUrl.length() : postfixIndex; return aUrl.substring(prefixIndex, postfixIndex); } public Proxy getProxy() { return proxy; } public TrackerClientData getRepositoryData() { return repositoryData; } public String getRepositoryUrl() { return mClient.getURL(); } /** * This queries PT to see if the given task has changed since the given date * * @param artitfactNamespaceAndType * @param taskId * @param dateString * @return * @throws Exception */ public boolean isChanged(String artitfactNamespaceAndType, String taskId, String dateString) throws Exception { List<String> ids = this.getChangedIds(artitfactNamespaceAndType, taskId, taskId, dateString); if (ids == null) return true; return ids.contains(taskId); } /** * Attach a file to a PT artifact * * @param taskId * @param comment * @param attachment * @throws ServiceException * @throws WSException * @throws RemoteException */ public long postAttachment(String taskId, String comment, DataSource attachment) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); AttachmentService service = new AttachmentServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Attachment"); AttachmentManager theService = service.getAttachmentService(portAddress); DataHandler attachmentHandler = new DataHandler(attachment); theService.addAttachment(taskId, attachment.getName(), comment, attachment.getContentType(), attachmentHandler); long[] ids = theService.getAttachmentIds(taskId); Arrays.sort(ids); return ids[ids.length - 1]; } public void removeAttachment(String artifactId, long attachmentId) throws ServiceException, WSException, RemoteException { EngineConfiguration config = mClient.getEngineConfiguration(); AttachmentService service = new AttachmentServiceLocator(config); URL portAddress = mClient.constructServiceURL("/tracker/Attachment"); AttachmentManager theService = service.getAttachmentService(portAddress); theService.removeAttachment(artifactId, attachmentId); } public void setRepositoryData(TrackerClientData data) { this.repositoryData = data; } /** * Get all of the server defined queries for the project * * @return * @param artifact * @param provideReason * @param reasonForCurrentlyProcessedArtifact * @param isFoundReasonForCurrentlyProcessedArtifact * @throws Exception * @throws Exception * * * NOTE: All artifact in the List must belong to the same * namespace and artifactType * */ public ClientArtifactListXMLHelper updateArtifactList(List<ClientArtifact> artifacts, long lastReadOn, boolean provideReason, boolean isFoundReasonForCurrentlyProcessedArtifact, String reasonForCurrentlyProcessedArtifact) throws Exception { Document doc = null; doc = createNewXMLDocument(DEFAULT_NAMESPACE, "ns1:" + "updateArtifactList"); ClientArtifactListXMLHelper helper = this.createOrUpdateArtifactList(doc, artifacts, lastReadOn, provideReason, isFoundReasonForCurrentlyProcessedArtifact, reasonForCurrentlyProcessedArtifact); return helper; } public void updateAttributes() { // TODO This should update the attribute information // This is not implemented since the attribute information is retrieved // per artifact right now, but would preferably be per repository. This // will also be needed for new artifact creation repositoryData.clear(); } /** * This is a check to ensure that invalid next page calls are not sent to * the server. * * @param pageInfo * @throws Exception */ public void validateNextPage(Node pageInfo, String altQueryRef) throws Exception { Node child = pageInfo.getFirstChild(); String msg = "Next page does not have a valid query reference"; String name, value; while (child != null) { name = child.getNodeName(); value = child.getTextContent(); if (name.contains("queryReference")) { if (value == null || value.length() < 1) { if (altQueryRef == null) throw new Exception(msg); child.setTextContent(altQueryRef); } return; } child = child.getNextSibling(); } throw new Exception(msg); } /** * Constructs a DOM Document from String * * @param contents * the XML document * @return the DOM representation of the document * @throws ParserConfigurationException * if the XML parser cannot be constructed * @throws SAXException * if the XML is not well formed * @throws IOException * if the read operation fails */ protected Document createDocument(String contents) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); contents = contents.replaceAll("__DEFAULT_NAMESPACE__", mClient.getDefaultNamespace()); InputSource inputSource = new InputSource(new StringReader(contents)); Document document = builder.parse(inputSource); return document; } private Document createNewXMLDocument(String namespace, String qualifiedTagName) { DocumentBuilderFactory dbf;// = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document doc = null; try { dbf = DocumentBuilderFactory.newInstance(); db = dbf.newDocumentBuilder(); DOMImplementation di = db.getDOMImplementation(); doc = di.createDocument(namespace, qualifiedTagName, null); } catch (ParserConfigurationException e) { log(e, "could not create document"); } return doc; } private boolean isVersionGreaterOrEqual(Version ver, String baselineVersion) { StringTokenizer serverTokens = new StringTokenizer(ver.getApiVersion(), "."); StringTokenizer clientTokens = new StringTokenizer(API_VERSION, "."); String serverVer = ""; String clientVer = ""; int sVersion, cVersion; while (serverTokens.hasMoreTokens()) { serverVer = serverTokens.nextToken(); clientVer = "0"; if (clientTokens.hasMoreTokens()) { clientVer = clientTokens.nextToken(); } sVersion = Integer.parseInt(serverVer); cVersion = Integer.parseInt(clientVer); if (cVersion > sVersion) return false; // test version is greater if (cVersion < sVersion) return true; // server ver is greater } return true; } private void log(Exception e, String string) { System.out.println(string + ": " + e.getMessage()); } private void printDocument(Document doc) { TransformerFactory tf = TransformerFactory.newInstance(); Transformer t; try { t = tf.newTransformer(); StringWriter sw = new StringWriter(); t.transform(new DOMSource(doc), new StreamResult(sw)); log.debug(sw.toString()); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } } private void sanitizeComments(ClientArtifact artifact) { List<ClientArtifactComment> comments = artifact.getComments(); if (comments != null) { Iterator<ClientArtifactComment> itComments = comments.iterator(); while (itComments.hasNext()) { ClientArtifactComment comment = itComments.next(); String commentText = comment.getCommentText(); if (StringUtils.isEmpty(commentText)) { itComments.remove(); } } } } /** * Converts a Response returned by the Dispatcher.execute operation to a * Document object. * * @param response * the Response returned by the Dispatcher.execute operation * @return the Document contained in the Response. * @throws Exception */ private Document toDocument(Response response) throws Exception { TrackerUtil.debug("response from server"); return response.get_any()[0].getAsDocument(); } /** * Converts a DOM Document to a Request object expected by the * Dispatcher.execute operation. * * @param document * the Document to convert to a Request object * @return the Request object */ private Request toRequest(Document document) { MessageElement element = new MessageElement(document.getDocumentElement()); return new Request(new MessageElement[] { element }); } /** * This checks to make sure the server version of the api is <= the client * version. If the server is downlevel, a tracker exception will be thrown * * @param ver * @throws TrackerException */ private void validateApiVersion(Version ver) throws TrackerException { if (isVersionGreaterOrEqual(ver, API_VERSION)) return; String errorMsg = "Your CollabNet server is running API version " + ver.getApiVersion() + ". This version is supported but will not allow required artifact attributes to be validated on the client."; throw new TrackerException(errorMsg); } }