diff options
Diffstat (limited to 'org.eclipse.mylyn.oslc.core/src/org/eclipse/mylyn/internal/oslc/core/client/AbstractOslcClient.java')
-rw-r--r-- | org.eclipse.mylyn.oslc.core/src/org/eclipse/mylyn/internal/oslc/core/client/AbstractOslcClient.java | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.oslc.core/src/org/eclipse/mylyn/internal/oslc/core/client/AbstractOslcClient.java b/org.eclipse.mylyn.oslc.core/src/org/eclipse/mylyn/internal/oslc/core/client/AbstractOslcClient.java new file mode 100644 index 000000000..a27f7e7e6 --- /dev/null +++ b/org.eclipse.mylyn.oslc.core/src/org/eclipse/mylyn/internal/oslc/core/client/AbstractOslcClient.java @@ -0,0 +1,570 @@ +/******************************************************************************* + * Copyright (c) 2009 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.oslc.core.client; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.httpclient.URIException; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.core.StatusHandler; +import org.eclipse.mylyn.commons.net.AbstractWebLocation; +import org.eclipse.mylyn.commons.net.Policy; +import org.eclipse.mylyn.commons.net.WebUtil; +import org.eclipse.mylyn.internal.oslc.core.IOslcCoreConstants; +import org.eclipse.mylyn.internal.oslc.core.OslcCreationDialogDescriptor; +import org.eclipse.mylyn.internal.oslc.core.OslcSelectionDialogDescriptor; +import org.eclipse.mylyn.internal.oslc.core.OslcServiceDescriptor; +import org.eclipse.mylyn.internal.oslc.core.OslcServiceFactory; +import org.eclipse.mylyn.internal.oslc.core.OslcServiceProvider; +import org.eclipse.mylyn.internal.oslc.core.OslcServiceProviderCatalog; +import org.eclipse.mylyn.internal.oslc.core.ServiceHome; +import org.eclipse.mylyn.internal.oslc.core.cm.AbstractChangeRequest; +import org.eclipse.mylyn.tasks.core.RepositoryResponse; +import org.eclipse.mylyn.tasks.core.data.TaskAttribute; +import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper; +import org.eclipse.mylyn.tasks.core.data.TaskData; +import org.jdom.Attribute; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.filter.ElementFilter; +import org.jdom.input.SAXBuilder; + +/** + * Base class from which to implement an OSLC client + * + * @author Robert Elves + */ +public abstract class AbstractOslcClient { + + protected final AbstractWebLocation location; + + protected final HttpClient httpClient; + + protected final OslcServiceDescriptor configuration; + + public AbstractOslcClient(AbstractWebLocation location, OslcServiceDescriptor data) { + this.location = location; + this.httpClient = createHttpClient(); + this.configuration = data; + } + + protected HttpClient createHttpClient() { + HttpClient httpClient = new HttpClient(); + httpClient.setHttpConnectionManager(WebUtil.getConnectionManager()); + httpClient.getParams().setCookiePolicy(CookiePolicy.RFC_2109); + + // See: https://jazz.net/jazz/web/projects/Rational%20Team%20Concert#action=com.ibm.team.workitem.viewWorkItem&id=85127\ + // Added to support fix session cookie issue when talking to tomcat + httpClient.getParams().setParameter("http.protocol.single-cookie-header", true); //$NON-NLS-1$ + + WebUtil.configureHttpClient(httpClient, getUserAgent()); + return httpClient; + } + + /** + * Return your unique connector identifier i.e. com.mycompany.myconnector + */ + public abstract String getUserAgent(); + + /** + * Exposed at connector level via IOslcCoreConnector.getAvailableServices() + */ + public List<OslcServiceProvider> getAvailableServices(String url, IProgressMonitor monitor) throws CoreException { + final List<OslcServiceProvider> result = new ArrayList<OslcServiceProvider>(); + + GetRequest request = new GetRequest(url) { + + @Override + public void run(GetMethod method, IProgressMonitor monitor) throws CoreException { + try { + parseServices(method.getResponseBodyAsStream(), result, monitor); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Network error occurred retrieving available services: " + e.getMessage(), e)); //$NON-NLS-1$ + } + } + }; + + request.execute(monitor); + + return result; + } + + protected Document getDocumentFromMethod(HttpMethodBase method) throws CoreException { + try { + return getDocumentFromStream(method.getResponseBodyAsStream()); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Network error obtaining response from server: " + e.getMessage(), e)); //$NON-NLS-1$ + } + } + + protected Document getDocumentFromStream(InputStream inStream) throws CoreException { + SAXBuilder builder = new SAXBuilder(); + builder.setExpandEntities(false); + try { + return builder.build(inStream); + } catch (JDOMException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Error parsing response: " + e.getMessage(), e)); //$NON-NLS-1$ + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Network error parsing response: " + e.getMessage(), e)); //$NON-NLS-1$ + } + } + + /** + * public for testing + */ + public void parseServices(InputStream inStream, Collection<OslcServiceProvider> providers, IProgressMonitor monitor) + throws CoreException { + + Document doc = getDocumentFromStream(inStream); + + Iterator<?> itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_SERVICE_PROVIDER_CATALOG)); + while (itr.hasNext()) { + Element element = (Element) itr.next(); + if (element.getParent() != doc.getRootElement()) { + Attribute attrResource = element.getAttribute(IOslcCoreConstants.ATTRIBUTE_RESOURCE, + IOslcCoreConstants.NAMESPACE_RDF); + String title = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC) + .getText(); + if (attrResource != null && attrResource.getValue().length() > 0) { + providers.add(new OslcServiceProviderCatalog(title, attrResource.getValue())); + } + } + } + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_SERVICE_PROVIDER)); + while (itr.hasNext()) { + Element element = (Element) itr.next(); + String title = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC) + .getText(); + Element service = element.getChild(IOslcCoreConstants.ELEMENT_SERVICES, + IOslcCoreConstants.NAMESPACE_OSLC_DISCOVERY_1_0); + if (service != null) { + String resource = service.getAttributeValue(IOslcCoreConstants.ATTRIBUTE_RESOURCE, + IOslcCoreConstants.NAMESPACE_RDF); + providers.add(new OslcServiceProvider(title, resource)); + } + } + } + + /** + * Retrieve a service descriptor for the given service provider. Exposed at connector level by + * IOslcConnector.getServiceDescriptor() + * + * @throws CoreException + */ + public OslcServiceDescriptor getServiceDescriptor(OslcServiceProvider provider, IProgressMonitor monitor) + throws CoreException { + OslcServiceDescriptor configuration = new OslcServiceDescriptor(provider.getUrl()); + downloadServiceDescriptor(configuration, monitor); + return configuration; + } + + /** + * Populate the provided configuration with new data from the remote repository. + */ + protected void downloadServiceDescriptor(final OslcServiceDescriptor config, IProgressMonitor monitor) + throws CoreException { + config.clear(); + + GetRequest request = new GetRequest(config.getAboutUrl()) { + + @Override + public void run(GetMethod method, IProgressMonitor monitor) throws CoreException { + try { + parseServiceDescriptor(method.getResponseBodyAsStream(), config, monitor); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Network error occurred while downloading service descriptor: " + e.getMessage(), e)); //$NON-NLS-1$ + } + } + }; + + request.execute(monitor); + } + + /** + * public for testing + */ + public void parseServiceDescriptor(InputStream inStream, OslcServiceDescriptor config, IProgressMonitor monitor) + throws CoreException { + Document doc = getDocumentFromStream(inStream); + + Iterator<?> itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_TITLE, + IOslcCoreConstants.NAMESPACE_DC)); + if (itr.hasNext()) { + Element element = (Element) itr.next(); + config.setTitle(element.getText()); + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_DESCRIPTION, + IOslcCoreConstants.NAMESPACE_DC)); + if (itr.hasNext()) { + Element element = (Element) itr.next(); + config.setDescription(element.getText()); + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_CREATIONDIALOG)); + while (itr.hasNext()) { + boolean isDefault = false; + Element element = (Element) itr.next(); + String label = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC) + .getText(); + String url = element.getChild(IOslcCoreConstants.ELEMENT_URL, IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0) + .getText(); + Attribute attrDefault = element.getAttribute(IOslcCoreConstants.ATTRIBUTE_DEFAULT, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (attrDefault != null && attrDefault.getValue().equals("true")) { //$NON-NLS-1$ + isDefault = true; + } + OslcCreationDialogDescriptor recordType = new OslcCreationDialogDescriptor(label, url); + config.addCreationDialog(recordType); + if (isDefault) { + config.setDefaultCreationDialog(recordType); + } + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_SIMPLEQUERY)); + if (itr.hasNext()) { + Element element = (Element) itr.next(); + String url = element.getChild(IOslcCoreConstants.ELEMENT_URL, IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0) + .getText(); + if (url != null) { + config.setSimpleQueryUrl(url); + } + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_FACTORY)); + while (itr.hasNext()) { + boolean isDefault = false; + Element element = (Element) itr.next(); + String title = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC) + .getText(); + String url = element.getChild(IOslcCoreConstants.ELEMENT_URL, IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0) + .getText(); + if (element.getAttribute(IOslcCoreConstants.ATTRIBUTE_DEFAULT) != null + && element.getAttribute(IOslcCoreConstants.ATTRIBUTE_DEFAULT).getValue().equals("true")) { //$NON-NLS-1$ + isDefault = true; + } + OslcServiceFactory factory = new OslcServiceFactory(title, url); + if (isDefault) { + config.setDefaultFactory(factory); + } + config.addServiceFactory(factory); + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_HOME)); + if (itr.hasNext()) { + Element element = (Element) itr.next(); + Element childTitle = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC); + Element childUrl = element.getChild(IOslcCoreConstants.ELEMENT_URL, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (childTitle != null && childTitle.getText().length() > 0 && childUrl != null + && childUrl.getText().length() > 0) { + ServiceHome home = new ServiceHome(childTitle.getText(), childUrl.getText()); + config.setHome(home); + } + } + + itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_SELECTIONDIALOG)); + if (itr.hasNext()) { + Element element = (Element) itr.next(); + Element childTitle = element.getChild(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC); + Element childUrl = element.getChild(IOslcCoreConstants.ELEMENT_URL, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (childTitle != null && childTitle.getText().length() > 0 && childUrl != null + && childUrl.getText().length() > 0) { + + OslcSelectionDialogDescriptor selection = new OslcSelectionDialogDescriptor(childTitle.getText(), + childUrl.getText()); + + String isDefault = element.getAttributeValue(IOslcCoreConstants.ATTRIBUTE_DEFAULT, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (isDefault != null) { + selection.setDefault(isDefault.equals("true")); //$NON-NLS-1$ + } + + String hintHeight = element.getAttributeValue(IOslcCoreConstants.ATTRIBUTE_HINTHEIGHT, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (hintHeight != null) { + selection.setHintHeight(hintHeight); + } + + String hintWidth = element.getAttributeValue(IOslcCoreConstants.ATTRIBUTE_HINTWIDTH, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (hintWidth != null) { + selection.setHintWidth(hintWidth); + } + + String label = element.getChildText(IOslcCoreConstants.ELEMENT_LABEL, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0); + if (label != null) { + selection.setLabel(label); + } + + config.addSelectionDialog(selection); + } + } + + } + + public Collection<AbstractChangeRequest> performQuery(String queryUrl, IProgressMonitor monitor) + throws CoreException { + final Collection<AbstractChangeRequest> result = new ArrayList<AbstractChangeRequest>(); + + GetRequest request = new GetRequest(queryUrl) { + + @Override + public void run(GetMethod method, IProgressMonitor monitor) throws CoreException { + try { + parseQueryResponse(method.getResponseBodyAsStream(), result, monitor); + // TODO: Handle pagination + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Network error occurred retrieving available services: " + e.getMessage(), e)); //$NON-NLS-1$ + } + } + }; + + request.execute(monitor); + + return result; + } + + // TODO: Handle pagination + public void parseQueryResponse(InputStream inStream, Collection<AbstractChangeRequest> requests, + IProgressMonitor monitor) throws CoreException { + Document doc = getDocumentFromStream(inStream); + + Iterator<?> itr = doc.getDescendants(new ElementFilter(IOslcCoreConstants.ELEMENT_CHANGEREQUEST, + IOslcCoreConstants.NAMESPACE_OSLC_CM_1_0)); + while (itr.hasNext()) { + Element element = (Element) itr.next(); + String title = element.getChildText(IOslcCoreConstants.ELEMENT_TITLE, IOslcCoreConstants.NAMESPACE_DC); + String id = element.getChildText(IOslcCoreConstants.ELEMENT_IDENTIFIER, IOslcCoreConstants.NAMESPACE_DC); + + if (title != null && id != null) { + AbstractChangeRequest request = createChangeRequest(id, title); + request.setType(element.getChildText(IOslcCoreConstants.ELEMENT_TYPE, IOslcCoreConstants.NAMESPACE_DC)); + request.setDescription(element.getChildText(IOslcCoreConstants.ELEMENT_DESCRIPTION, + IOslcCoreConstants.NAMESPACE_DC)); + request.setSubject(element.getChildText(IOslcCoreConstants.ELEMENT_SUBJECT, + IOslcCoreConstants.NAMESPACE_DC)); + request.setCreator(element.getChildText(IOslcCoreConstants.ELEMENT_CREATOR, + IOslcCoreConstants.NAMESPACE_DC)); + request.setModified(element.getChildText(IOslcCoreConstants.ELEMENT_MODIFIED, + IOslcCoreConstants.NAMESPACE_DC)); + requests.add(request); + } + + } + + } + + protected abstract AbstractChangeRequest createChangeRequest(String id, String title); + + /** + * Updates this clients 'repository configuration'. If old types were in use (locally cached) and still exist they + * are re-read from repository. + */ + public void updateRepositoryConfiguration(IProgressMonitor monitor) throws CoreException { + configuration.clear(); + downloadServiceDescriptor(configuration, monitor); + } + + public abstract TaskData getTaskData(final String encodedTaskId, TaskAttributeMapper mapper, + IProgressMonitor monitor) throws CoreException; + + public abstract RepositoryResponse putTaskData(TaskData taskData, Set<TaskAttribute> oldValues, + IProgressMonitor monitor) throws CoreException; + + protected abstract class GetRequest extends HttpRequest<GetMethod> { + + public GetRequest(String resourceUrl) { + super(resourceUrl); + } + + @Override + public GetMethod constructMethod(String requestPath) { + return new GetMethod(requestPath); + } + } + + protected abstract class PutRequest extends HttpRequest<PutMethod> { + + private final RequestEntity entity; + + private final Map<String, String> requestHeaders = new HashMap<String, String>(); + + public PutRequest(String resourceUrl, RequestEntity entity) { + super(resourceUrl); + this.entity = entity; + } + + public void setRequestHeader(String key, String value) { + requestHeaders.put(key, value); + } + + @Override + public PutMethod constructMethod(String requestPath) { + PutMethod method = new PutMethod(requestPath); + method.setRequestEntity(entity); + for (String key : requestHeaders.keySet()) { + method.addRequestHeader(key, requestHeaders.get(key)); + } + return method; + } + } + + protected abstract class PostRequest extends HttpRequest<PostMethod> { + + private final NameValuePair[] pairs; + + private RequestEntity entity; + + public PostRequest(String resourceUrl, NameValuePair[] pairs) { + super(resourceUrl); + this.pairs = pairs; + } + + @Override + public PostMethod constructMethod(String requestPath) { + PostMethod method = new PostMethod(requestPath); + this.entity = getRequestEntity(method); + if (pairs != null) { + method.setRequestBody(pairs); + } else if (entity != null) { + method.setRequestEntity(entity); + } else { + StatusHandler.log(new Status(IStatus.WARNING, IOslcCoreConstants.ID_PLUGIN, + "Request body or entity missing upon post.")); //$NON-NLS-1$ + } + return method; + } + + public RequestEntity getRequestEntity(PostMethod method) { + return null; + } + } + + protected void handleReturnCode(int code, HttpMethodBase method) throws CoreException { + try { + if (code == java.net.HttpURLConnection.HTTP_OK) { + return;// Status.OK_STATUS; + } else if (code == java.net.HttpURLConnection.HTTP_MOVED_TEMP + || code == java.net.HttpURLConnection.HTTP_CREATED) { + // A new resource created... + return;// Status.OK_STATUS; + } else if (code == java.net.HttpURLConnection.HTTP_UNAUTHORIZED + || code == java.net.HttpURLConnection.HTTP_FORBIDDEN) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Unable to log into server, ensure repository credentials are correct.")); //$NON-NLS-1$ + } else if (code == java.net.HttpURLConnection.HTTP_PRECON_FAILED) { + // Mid-air collision + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Mid-air collision occurred.")); //$NON-NLS-1$ + } else if (code == java.net.HttpURLConnection.HTTP_CONFLICT) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, "A conflict occurred.")); //$NON-NLS-1$ + } else { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, + "Unknown error occurred. Http Code: " + code + " Request: " + method.getURI() + " Response: " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + method.getResponseBodyAsString())); + } + } catch (URIException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, "Network Error: " //$NON-NLS-1$ + + e.getMessage())); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IOslcCoreConstants.ID_PLUGIN, "Network Error: " //$NON-NLS-1$ + + e.getMessage())); + } + } + + protected abstract class HttpRequest<T extends HttpMethodBase> { + + private T method; + + private HostConfiguration hostConfiguration; + + private final String url; + + public HttpRequest(String url) { + this.url = url; + } + + public void execute(IProgressMonitor monitor) throws CoreException { + monitor = Policy.monitorFor(monitor); + try { + monitor.beginTask(getTaskName(), IProgressMonitor.UNKNOWN); + method = constructMethod(getRequestPath(url)); + method.setFollowRedirects(false); + method.setDoAuthentication(true); + // application/xml is returned by oslc servers by default + //method.setRequestHeader("Accept", "application/xml"); + hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor); + int code = WebUtil.execute(httpClient, hostConfiguration, method, monitor); + + handleReturnCode(code, method); + run(method, monitor); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.WARNING, IOslcCoreConstants.ID_PLUGIN, + "An unexpected network error has occurred: " + e.getMessage(), e)); //$NON-NLS-1$ + } finally { + if (method != null) { + method.releaseConnection(); + } + monitor.done(); + } + + } + + protected String getTaskName() { + return "Retrieving from server..."; //$NON-NLS-1$ + } + + public abstract void run(T method, IProgressMonitor monitor) throws CoreException, IOException; + + public abstract T constructMethod(String requestPath); + + } + + public String getRequestPath(String repositoryUrl) { + if (repositoryUrl.startsWith("./")) { //$NON-NLS-1$ + return WebUtil.getRequestPath(location.getUrl()) + repositoryUrl.substring(1); + } else if (repositoryUrl.startsWith("/")) { //$NON-NLS-1$ + return WebUtil.getRequestPath(location.getUrl()) + repositoryUrl; + } + return WebUtil.getRequestPath(repositoryUrl); + } +}
\ No newline at end of file |