From 9ccd593403985d518253dda4598a52960444cbbd Mon Sep 17 00:00:00 2001 From: slewis Date: Fri, 11 Sep 2009 17:00:09 +0000 Subject: Initial checkin of REST api work from google soc project 2009 from Holger Staudacher. CQ for IP review/approval of contribution: http://dev.eclipse.org/ipzilla/show_bug.cgi?id=3504 --- .../org.eclipse.ecf.remoteservice.rest/.classpath | 7 + .../org.eclipse.ecf.remoteservice.rest/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 69 ++++ .../META-INF/MANIFEST.MF | 29 ++ .../build.properties | 6 + .../org.eclipse.ecf.remoteservice.rest/plugin.xml | 20 + .../ecf/internal/remoteservice/rest/Activator.java | 70 ++++ .../rest/ResourceRepresentationFactory.java | 113 ++++++ .../remoteservice/rest/RestServiceRegistry.java | 171 +++++++++ .../eclipse/ecf/remoteservice/rest/IRestCall.java | 105 ++++++ .../remoteservice/rest/IRestResponseProcessor.java | 26 ++ .../ecf/remoteservice/rest/RestContainer.java | 357 ++++++++++++++++++ .../rest/RestContainerInstantiatior.java | 60 +++ .../ecf/remoteservice/rest/RestService.java | 409 +++++++++++++++++++++ .../remoteservice/rest/RestServiceReference.java | 51 +++ .../rest/RestServiceRegistration.java | 106 ++++++ .../ecf/remoteservice/rest/identity/RestID.java | 96 +++++ .../remoteservice/rest/identity/RestNamespace.java | 81 ++++ .../remoteservice/rest/resource/IRestResource.java | 38 ++ .../remoteservice/rest/resource/XMLResource.java | 55 +++ .../remoteservice/rest/util/DeleteRestCall.java | 44 +++ .../ecf/remoteservice/rest/util/GetRestCall.java | 50 +++ .../ecf/remoteservice/rest/util/PostRestCall.java | 46 +++ .../ecf/remoteservice/rest/util/PutRestCall.java | 43 +++ .../ecf/remoteservice/rest/util/RestCall.java | 77 ++++ .../remoteservice/rest/util/RestCallFactory.java | 107 ++++++ .../remoteservice/rest/util/RestRemoteCall.java | 39 ++ 27 files changed, 2303 insertions(+) create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/.classpath create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/.project create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/.settings/org.eclipse.jdt.core.prefs create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/META-INF/MANIFEST.MF create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/build.properties create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/plugin.xml create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/Activator.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/ResourceRepresentationFactory.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/RestServiceRegistry.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestResponseProcessor.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainer.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainerInstantiatior.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestService.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceReference.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceRegistration.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestID.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestNamespace.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/IRestResource.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/XMLResource.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/DeleteRestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/GetRestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PostRestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PutRestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCall.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCallFactory.java create mode 100644 framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestRemoteCall.java (limited to 'framework/bundles/org.eclipse.ecf.remoteservice.rest') diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/.classpath b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.classpath new file mode 100644 index 000000000..2fbb7a23e --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/.project b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.project new file mode 100644 index 000000000..461918136 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.project @@ -0,0 +1,28 @@ + + + org.eclipse.ecf.internal.remoteservice.rest + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/.settings/org.eclipse.jdt.core.prefs b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..7fae09b79 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,69 @@ +#Wed Aug 19 11:43:46 PDT 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.3 diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/META-INF/MANIFEST.MF b/framework/bundles/org.eclipse.ecf.remoteservice.rest/META-INF/MANIFEST.MF new file mode 100644 index 000000000..c280292d8 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: REST API based on the remote service API +Bundle-SymbolicName: org.eclipse.ecf.remoteservice.rest;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.eclipse.ecf.internal.remoteservice.rest.Activator +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: J2SE-1.4 +Export-Package: org.eclipse.ecf.internal.remoteservice.rest;x-internal:=true, + org.eclipse.ecf.remoteservice.rest, + org.eclipse.ecf.remoteservice.rest.identity, + org.eclipse.ecf.remoteservice.rest.resource, + org.eclipse.ecf.remoteservice.rest.util +Import-Package: org.apache.commons.httpclient;version="3.0.1", + org.apache.commons.httpclient.auth;version="3.0.1", + org.apache.commons.httpclient.methods;version="3.0.1", + org.apache.commons.httpclient.params;version="3.0.1", + org.eclipse.core.runtime;version="3.4.0", + org.eclipse.ecf.core.sharedobject, + org.eclipse.ecf.provider.generic, + org.eclipse.ecf.provider.remoteservice.generic, + org.eclipse.ecf.remoteservice, + org.eclipse.ecf.remoteservice.events, + org.eclipse.ecf.remoteservice.util, + org.eclipse.ecf.remoteservice.util.tracker, + org.eclipse.equinox.concurrent.future;version="1.0.0", + org.osgi.framework;version="1.5.0", + org.osgi.util.tracker;version="1.4.2" +Require-Bundle: org.eclipse.ecf;bundle-version="3.0.0" diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/build.properties b/framework/bundles/org.eclipse.ecf.remoteservice.rest/build.properties new file mode 100644 index 000000000..2dcc4bdc6 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + bin/ diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/plugin.xml b/framework/bundles/org.eclipse.ecf.remoteservice.rest/plugin.xml new file mode 100644 index 000000000..747f90590 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/plugin.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/Activator.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/Activator.java new file mode 100644 index 000000000..c63626ea5 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/Activator.java @@ -0,0 +1,70 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.internal.remoteservice.rest; + +import org.eclipse.ecf.remoteservice.rest.resource.IRestResource; +import org.eclipse.ecf.remoteservice.rest.resource.XMLResource; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator implements BundleActivator { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.ecf.remoteservice.rest"; + + // The shared instance + private static Activator plugin; + + private BundleContext context; + + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + plugin = this; + this.context = context; + registerXMLService( context ); + } + + private void registerXMLService(BundleContext context) { + IRestResource xmlResource = new XMLResource(); + context.registerService(IRestResource.class.getName(), xmlResource, null); + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static synchronized Activator getDefault() { + if(plugin == null) { + plugin = new Activator(); + } + return plugin; + } + + public BundleContext getContext() { + return context; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/ResourceRepresentationFactory.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/ResourceRepresentationFactory.java new file mode 100644 index 000000000..b193cbf36 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/ResourceRepresentationFactory.java @@ -0,0 +1,113 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.internal.remoteservice.rest; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.HttpMethod; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.ecf.remoteservice.rest.IRestCall; +import org.eclipse.ecf.remoteservice.rest.resource.IRestResource; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class ResourceRepresentationFactory implements IAdaptable { + + private static ResourceRepresentationFactory factory; + private List resources = new ArrayList(); + + private class RestResourceTracker extends ServiceTracker { + + public RestResourceTracker(BundleContext context) { + super(context, IRestResource.class.getName(), null); + } + + public Object addingService(ServiceReference reference) { + Object service = context.getService(reference); + if(service != null && service instanceof IRestResource) { + if(!resources.contains(service)) + resources.add(service); + } + return service; + } + + public void removedService(ServiceReference reference, Object service) { + resources.remove(service); + context.ungetService(reference); + } + + } + + private ResourceRepresentationFactory() { + + } + + public static ResourceRepresentationFactory getDefault() { + if(factory == null) { + factory = new ResourceRepresentationFactory(); + factory.init(); + } + return factory; + } + + private void init() { + Activator activator = Activator.getDefault(); + BundleContext context = activator.getContext(); + try { + ServiceReference[] references = context.getServiceReferences(IRestResource.class.getName(), null); + for (int i = 0; i < references.length; i++) { + Object service = context.getService(references[i]); + if(service instanceof IRestResource) + resources.add((IRestResource)service); + } + } catch (InvalidSyntaxException e) { + e.printStackTrace(); + } + final ServiceTracker resourceTracker = new RestResourceTracker(context); + resourceTracker.open(); + activator.getContext().addBundleListener(new BundleListener() { + + public void bundleChanged(BundleEvent event) { + if(event.getType() == BundleEvent.STOPPING) + resourceTracker.close(); + } + }); + } + + /** + * Creates a resource representation for the resource defined in {@link IRestCall}'s + * getEstimatedResourceIdentifier() Method. This will be compared with all + * registered services of the type {@link IRestResource} by calling their getIdentifier() + * methods. If a service matches the estimated identifier it's parse method will + * be invoked to parse the content of the resource. + */ + public Object createResourceRepresentation(HttpMethod method, IRestCall restCall) throws ParseException, IOException { + for (int i = 0; i < resources.size(); i++) { + IRestResource resource = (IRestResource)resources.get(i); + if(resource.getIdentifier().equals(restCall.getEstimatedResourceIdentifier())) + return resource.createRepresentation(method.getResponseBodyAsString()); + } + return null; + } + + public Object getAdapter(Class adapter) { + if(adapter == List.class) + return resources; + return null; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/RestServiceRegistry.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/RestServiceRegistry.java new file mode 100644 index 000000000..11ea9ade5 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/internal/remoteservice/rest/RestServiceRegistry.java @@ -0,0 +1,171 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.internal.remoteservice.rest; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.ecf.core.ContainerConnectException; +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.provider.remoteservice.generic.RemoteFilterImpl; +import org.eclipse.ecf.remoteservice.IRemoteFilter; +import org.eclipse.ecf.remoteservice.IRemoteService; +import org.eclipse.ecf.remoteservice.IRemoteServiceID; +import org.eclipse.ecf.remoteservice.IRemoteServiceReference; +import org.eclipse.ecf.remoteservice.IRemoteServiceRegistration; +import org.eclipse.ecf.remoteservice.rest.RestContainer; +import org.eclipse.ecf.remoteservice.rest.RestServiceReference; +import org.eclipse.ecf.remoteservice.rest.RestServiceRegistration; +import org.osgi.framework.InvalidSyntaxException; + +/** + * A registry for RestServices to fit the remote service API. + */ +public class RestServiceRegistry implements Serializable { + + private static final long serialVersionUID = -7002609161000008043L; + private static long nextServiceId = 0L; + private ID containerId; + private List registrations; + private RestContainer container; + private IConnectContext connectContext; + + public RestServiceRegistry(RestContainer container) { + this.containerId = container.getID(); + this.container = container; + registrations = new ArrayList(); + } + + public RestContainer getContainer() { + return container; + } + + public long getNextServiceId() { + return nextServiceId++; + } + + public ID getContainerId() { + return containerId; + } + + public void registerRegistration(RestServiceRegistration registration) { + if(!registrations.contains(registration)) + registrations.add(registration); + } + + public void unregisterRegistration(RestServiceRegistration registration) { + registrations.remove(registration); + } + + public IRemoteService findService(IRemoteServiceReference reference) { + for (int i = 0; i < registrations.size(); i++) { + RestServiceRegistration reg = (RestServiceRegistration) registrations.get(i); + if( reg.getReference().equals(reference)) + return reg.getService(); + } + return null; + } + + public IRemoteServiceReference findServiceReference(IRemoteServiceID serviceID) { + for (int i = 0; i < registrations.size(); i++) { + IRemoteServiceRegistration reg = (IRemoteServiceRegistration)registrations.get(i); + if( serviceID.equals(reg.getID())) + return reg.getReference(); + } + return null; + } + + public IRemoteServiceReference[] getRemoteServiceReferences(ID target, String clazz, String filter) { + if (target == null) + return getRemoteServiceReferences((ID[]) null, clazz, filter); + // If we're not already connected, then connect to targetID + // If we *are* already connected, then we do *not* connect to target, but rather just search for targetID/endpoint + if (container.getConnectedID() == null) { + try { + container.connect(target, connectContext); + } catch (ContainerConnectException e) { + e.printStackTrace(); + } + } + // Now we're connected (or already were connected), so we look for remote service references for target + return getRemoteServiceReferences(new ID[] {target}, clazz, filter); + } + + public IRemoteServiceReference[] getRemoteServiceReferences(ID[] idFilter, String clazz, String filter) { + if (clazz == null) + return null; + List result = new ArrayList(); + IRemoteFilter remoteFilter = null; + try { + remoteFilter = (filter == null) ? null : new RemoteFilterImpl(filter); + } catch (InvalidSyntaxException e) { + e.printStackTrace(); + } + for (int i = 0; i < registrations.size(); i++) { + RestServiceRegistration reg = (RestServiceRegistration)registrations.get(i); + if(idFilter == null || containsID( reg, idFilter)) { + String[] clazzes = reg.getClazzes(); + boolean found = false; + for (int j = 0; j < clazzes.length && !found; j++) { + if(clazz.equals(clazzes[j]) && !result.contains(reg.getReference())) { + result.add(reg.getReference()); + found = true; + } + } + } + } + // check the filter + if(remoteFilter != null){ + for (int i = 0; i < result.size(); i++) { + RestServiceReference ref = (RestServiceReference)result.get(i); + if(!remoteFilter.match(ref)) + result.remove(i); + } + } + if(result.size() > 0 ) { + RestServiceReference[] array = new RestServiceReference[result.size()]; + result.toArray(array); + return (array.length == 0) ? null : array; + } + return null; + } + + private boolean containsID(RestServiceRegistration reg, ID[] idFilter) { + for (int i = 0; i < idFilter.length; i++) { + if(reg.getID().equals(idFilter[i])) + return true; + } + return false; + } + + public IRemoteServiceID getRemoteServiceID(ID containerID, long containerRelativeID) { + if(containerID.equals(this.containerId)) { + for (int i = 0; i < registrations.size(); i++) { + RestServiceRegistration reg = (RestServiceRegistration) registrations.get(i); + if(reg.getID().getContainerRelativeID() == containerRelativeID) + return reg.getID(); + } + } + return null; + } + + public String[] getClazzes(IRemoteServiceReference reference) { + for (int i = 0; i < registrations.size(); i++) { + RestServiceRegistration reg = (RestServiceRegistration) registrations.get(i); + if(reg.getReference().equals(reference)) { + return reg.getClazzes(); + } + } + return null; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestCall.java new file mode 100644 index 000000000..ab17f88dd --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestCall.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.util.RestCallFactory; + +/** + * This class acts as a container for a specific REST service call. It can be + * implemented from scratch or created via {@link RestCallFactory}. + */ +public interface IRestCall { + + /** + * Http GET method. + */ + public static final String HTTP_GET = "GET"; + /** + * Http POST method. + */ + public static final String HTTP_POST = "POST"; + /** + * Http PUT method. + */ + public static final String HTTP_PUT = "PUT"; + /** + * Htpp DELETE method. + */ + public static final String HTTP_DELETE = "DELETE"; + + /** + * Every rest web service returns a resource representation. I.e a + * representation as a xml file from a xml resource. + * + * @return the identifier for a estimated resource, i.e. + * ecf.rest.resource.xml. Will not be null. + */ + public String getEstimatedResourceIdentifier(); + + /** + * Defines the URI for the rest service. This should be a unique URI which + * belongs to a rest service i.e on the web. + * + * @return the URI of a rest service. Will not be null. + */ + public URI getURI(); + + /** + * Defines the HTTP method for this type of rest call. For example a GET or + * POST method. + * + * @return the HTTP Method as one of the IRestCall HTTP_* constants. Will + * not be null. + */ + public String getMethod(); + + /** + * Defines the call specific request headers. + * + * @return a {@link Map} object which contains the header parameters. May be + * null. + */ + public Map getRequestHeaders(); + + /** + * Defines the key-value pairs parameter for this call. A Rest Call may + * either have parameters or a RequestEntity (mutual exclusive). + * + * @return an array containing key-value pairs in the format: key=value. May + * be null. + * + * @see #getRequestEntity() + */ + public Object[] getParameters(); + + /** + * Get timeout (in ms) for the rest call. + * + * @return long timeout in ms + */ + public long getTimeout(); + + /** + * Gets the request entity. The RequestEntity is typically used to transmit + * data by HTTP_POST and HTTP_PUT calls. HTTP_GET calls may not use a + * request entity A. Rest Call may either have parameters or a RequestEntity + * (mutual exclusive) + * + * @return the request entity + * + * @see RequestEntity + */ + public RequestEntity getRequestEntity(); + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestResponseProcessor.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestResponseProcessor.java new file mode 100644 index 000000000..eaee3edbe --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/IRestResponseProcessor.java @@ -0,0 +1,26 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +/** + * If a POJO is used as a REST service object than it has to implement this interface + * to get called if a response from the service was received. Otherwise the POJO + * gets no content. + */ +public interface IRestResponseProcessor { + + /** + * This method is called if the response from a rest service was received. + * + * @param response the parsed resource representation. + */ + public void processResource(Object response); + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainer.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainer.java new file mode 100644 index 000000000..2e94d20d4 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainer.java @@ -0,0 +1,357 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ecf.core.AbstractContainer; +import org.eclipse.ecf.core.ContainerConnectException; +import org.eclipse.ecf.core.IContainer; +import org.eclipse.ecf.core.events.ContainerConnectedEvent; +import org.eclipse.ecf.core.events.ContainerConnectingEvent; +import org.eclipse.ecf.core.events.ContainerDisconnectedEvent; +import org.eclipse.ecf.core.events.ContainerDisconnectingEvent; +import org.eclipse.ecf.core.events.ContainerDisposeEvent; +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.identity.Namespace; +import org.eclipse.ecf.core.jobs.JobsExecutor; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.core.util.ECFException; +import org.eclipse.ecf.internal.remoteservice.rest.RestServiceRegistry; +import org.eclipse.ecf.remoteservice.IRemoteCall; +import org.eclipse.ecf.remoteservice.IRemoteFilter; +import org.eclipse.ecf.remoteservice.IRemoteService; +import org.eclipse.ecf.remoteservice.IRemoteServiceContainer; +import org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter; +import org.eclipse.ecf.remoteservice.IRemoteServiceID; +import org.eclipse.ecf.remoteservice.IRemoteServiceListener; +import org.eclipse.ecf.remoteservice.IRemoteServiceReference; +import org.eclipse.ecf.remoteservice.IRemoteServiceRegistration; +import org.eclipse.ecf.remoteservice.RemoteServiceContainer; +import org.eclipse.ecf.remoteservice.events.IRemoteServiceEvent; +import org.eclipse.ecf.remoteservice.events.IRemoteServiceRegisteredEvent; +import org.eclipse.ecf.remoteservice.events.IRemoteServiceUnregisteredEvent; +import org.eclipse.ecf.remoteservice.rest.identity.RestID; +import org.eclipse.ecf.remoteservice.util.RemoteFilterImpl; +import org.eclipse.equinox.concurrent.future.IExecutor; +import org.eclipse.equinox.concurrent.future.IFuture; +import org.eclipse.equinox.concurrent.future.IProgressRunnable; +import org.osgi.framework.InvalidSyntaxException; + +/** + * A container for REST services. This was implemented using the ECF#RemoteServiceAPI. + */ +public class RestContainer extends AbstractContainer implements IRemoteServiceContainerAdapter, IRemoteServiceContainer { + + public static final String NAME = "ecf.rest.client"; + private ID connectedId; + private ID id; + private List remoteServiceListeners = new ArrayList(); + private List containerListeners = new ArrayList(); + private RestServiceRegistry registry; + private List referencesInUse = new ArrayList(); + private RemoteServiceContainer remoteServiceContainer; + private IConnectContext connectContext; + private URL originBaseUrl; + private Map restCalls = new HashMap(); + + public RestContainer(ID id) { + this.id = id; + remoteServiceContainer = new RemoteServiceContainer(this, this); + registry = new RestServiceRegistry(this); + } + + public RestContainer() { + } + + public void connect(ID targetID, IConnectContext connectContext) throws ContainerConnectException { + fireContainerEvent(new ContainerConnectingEvent(id, targetID)); + if(targetID instanceof RestID){ + URL baseURL = ((RestID)targetID).getBaseURL(); + if(this.id instanceof RestID) { + RestID restId = (RestID)this.id; + originBaseUrl = restId.getBaseURL(); + restId.setBaseUrl(baseURL); + } + } + connectedId = targetID; + this.connectContext = connectContext; + fireContainerEvent(new ContainerConnectedEvent(id, targetID)); + } + + public void disconnect() { + ID oldId = connectedId; + fireContainerEvent(new ContainerDisconnectingEvent(id, oldId)); + connectedId = null; + connectContext = null; + if(id instanceof RestID) + ((RestID)id).setBaseUrl(originBaseUrl); + fireContainerEvent(new ContainerDisconnectedEvent(id, oldId)); + } + + public void dispose() { + disconnect(); + fireContainerEvent(new ContainerDisposeEvent(id)); + containerListeners.clear(); + remoteServiceListeners.clear(); + + } + + void fireRemoteServiceEvent(IRemoteServiceEvent event) { + synchronized (remoteServiceListeners) { + for (int i = 0; i < remoteServiceListeners.size(); i++) { + ((IRemoteServiceListener) remoteServiceListeners.get(i)).handleServiceEvent(event); + } + } + } + + public Namespace getConnectNamespace() { + if( connectedId != null ) + return connectedId.getNamespace(); + return null; + } + + public ID getConnectedID() { + return connectedId; + } + + + public ID getID() { + return id; + } + + public void addRemoteServiceListener(IRemoteServiceListener listener) { + remoteServiceListeners.add(listener); + } + + public IFuture asyncGetRemoteServiceReferences(final ID[] idFilter, final String clazz, final String filter) { + IExecutor executor = new JobsExecutor("asyncGetRemoteServiceReferences"); //$NON-NLS-1$ + return executor.execute(new IProgressRunnable() { + public Object run(IProgressMonitor monitor) throws Exception { + return getRemoteServiceReferences(idFilter, clazz, filter); + } + }, null); + } + + public IFuture asyncGetRemoteServiceReferences(final ID target, final String clazz, final String filter) { + IExecutor executor = new JobsExecutor("asyncGetRemoteServiceReferences"); //$NON-NLS-1$ + return executor.execute(new IProgressRunnable() { + public Object run(IProgressMonitor monitor) throws Exception { + return getRemoteServiceReferences(target, clazz, filter); + } + }, null); + } + + public IRemoteFilter createRemoteFilter(String filter) throws InvalidSyntaxException { + return new RemoteFilterImpl(filter); + } + + public IRemoteServiceReference[] getAllRemoteServiceReferences(String clazz, String filter) throws InvalidSyntaxException { + return null; + } + + public IRemoteService getRemoteService(IRemoteServiceReference reference) { + IRemoteService service = registry.findService(reference); + if(service != null) + referencesInUse.add(reference); + return service; + } + + public IRemoteServiceID getRemoteServiceID(ID containerID, long containerRelativeID) { + return registry.getRemoteServiceID( containerID, containerRelativeID); + } + + public Namespace getRemoteServiceNamespace() { + if(id == null) + return null; + return id.getNamespace(); + } + + public IRemoteServiceReference getRemoteServiceReference(IRemoteServiceID serviceID) { + return registry.findServiceReference(serviceID); + } + + public IRemoteServiceReference[] getRemoteServiceReferences(ID[] idFilter, String clazz, String filter) throws InvalidSyntaxException { + return registry.getRemoteServiceReferences(idFilter, clazz, filter); + } + + public IRemoteServiceReference[] getRemoteServiceReferences(ID target, String clazz, String filter) throws InvalidSyntaxException, ContainerConnectException { + return registry.getRemoteServiceReferences( target, clazz, filter); + } + + public IRemoteServiceRegistration registerRemoteService(final String[] clazzes, Object service, Dictionary properties) { + final RestServiceRegistration registration = new RestServiceRegistration(clazzes, service, properties, registry); + fireRemoteServiceEvent(new IRemoteServiceRegisteredEvent() { + + public IRemoteServiceReference getReference() { + return registration.getReference(); + } + + public ID getLocalContainerID() { + return registration.getContainerID(); + } + + public ID getContainerID() { + return id; + } + + public String[] getClazzes() { + return clazzes; + } + }); + if(service instanceof RestService) + ((RestService)service).setReference(registration.getReference()); + registry.registerRegistration(registration); + return registration; + } + + public void removeRemoteServiceListener(IRemoteServiceListener listener) { + remoteServiceListeners.remove(listener); + } + + public void setConnectContextForAuthentication(IConnectContext connectContext) { + this.connectContext = connectContext; + } + + public boolean ungetRemoteService(final IRemoteServiceReference reference) { + boolean result = referencesInUse.contains(reference); + referencesInUse.remove(reference); + fireRemoteServiceEvent(new IRemoteServiceUnregisteredEvent() { + + public IRemoteServiceReference getReference() { + return reference; + } + + public ID getLocalContainerID() { + return id; + } + + public ID getContainerID() { + return id; + } + + public String[] getClazzes() { + return registry.getClazzes( reference ); + } + }); + return result; + } + + public IContainer getContainer() { + return remoteServiceContainer.getContainer(); + } + + public IRemoteServiceContainerAdapter getContainerAdapter() { + return remoteServiceContainer.getContainerAdapter(); + } + + public IRemoteService getRemoteService(String targetLocation, String serviceInterfaceClass, String filter) throws ContainerConnectException, InvalidSyntaxException { + return remoteServiceContainer.getRemoteService(targetLocation, serviceInterfaceClass, filter); + } + + public IRemoteService getRemoteService(String targetLocation, String serviceInterfaceClass) throws ContainerConnectException { + return remoteServiceContainer.getRemoteService(targetLocation, serviceInterfaceClass); + } + + public IRemoteService getRemoteService(String serviceInterfaceClass) { + return remoteServiceContainer.getRemoteService(serviceInterfaceClass); + } + + IConnectContext getConnectContext() { + return connectContext; + } + + public IRestCall lookupRestCall(IRemoteCall call) { + String key = call.getMethod(); + return (IRestCall) restCalls.get(key); + } + + /** + * Registers a POJO object as a REST service. The service object has to implement + * {@link IRestResponseProcessor} to get notified about an incoming response. + * This method also registers a {@link IRemoteService} object for the rest service. + * To associate a {@link IRemoteCall} to the rest service object the + * {@link IRemoteCall#getMethod()} will be used. Therefore a {@link Map} has to + * passed to this method with a {@link String} as key and an {@link IRestCall} object + * as value. + * + * @param clazzes a String array woth classnames which the service object has to implement. + * @param service the service object as a POJO + * @param restCalls a {@link Map} with String as keys and {@link IRestCall} objects as value. + * @param properties to be associated with service. + * + * @return The service registration for the registered {@link IRemoteService}. + * Will not return null. + */ + public RestServiceRegistration registerRestService( String[] clazzes, Object service, Map restCalls, Dictionary properties) throws ECFException { + if(checkServiceClass(clazzes, service) == null && service instanceof IRestResponseProcessor) { + // map the keys to the restCalls + Object[] keys = restCalls.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + Object restCall = restCalls.get(keys[i]); + if(!(restCall instanceof IRestCall)) + throw new ECFException("registered calls must be instacnes of IRestCall"); + this.restCalls.put(keys[i], restCall); + } + RestService restService = new RestService(service); + return (RestServiceRegistration) registerRemoteService(new String[]{RestService.class.getName()}, restService, properties); + } else + throw new IllegalArgumentException("service does not implement all classes or IRestResponseProcessor"); + } + + /** + * Return the name of the class that is not satisfied by the service object. + * @param clazzes Array of class names. + * @param serviceObject Service object. + * @return The name of the class that is not satisfied by the service object. + */ + private String checkServiceClass(final String[] clazzes, final Object serviceObject) { + ClassLoader cl = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return serviceObject.getClass().getClassLoader(); + } + }); + for (int i = 0, len = clazzes.length; i < len; i++) { + try { + Class serviceClazz = cl == null ? Class.forName(clazzes[i]) : cl.loadClass(clazzes[i]); + if (!serviceClazz.isInstance(serviceObject)) + return clazzes[i]; + } catch (ClassNotFoundException e) { + //This check is rarely done + if (extensiveCheckServiceClass(clazzes[i], serviceObject.getClass())) + return clazzes[i]; + } + } + return null; + } + + private static boolean extensiveCheckServiceClass(String clazz, Class serviceClazz) { + if (clazz.equals(serviceClazz.getName())) + return false; + Class[] interfaces = serviceClazz.getInterfaces(); + for (int i = 0, len = interfaces.length; i < len; i++) + if (!extensiveCheckServiceClass(clazz, interfaces[i])) + return false; + Class superClazz = serviceClazz.getSuperclass(); + if (superClazz != null) + if (!extensiveCheckServiceClass(clazz, superClazz)) + return false; + return true; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainerInstantiatior.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainerInstantiatior.java new file mode 100644 index 000000000..d4c293dbd --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestContainerInstantiatior.java @@ -0,0 +1,60 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import java.net.URL; + +import org.eclipse.ecf.core.ContainerCreateException; +import org.eclipse.ecf.core.ContainerTypeDescription; +import org.eclipse.ecf.core.IContainer; +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.provider.IContainerInstantiator; +import org.eclipse.ecf.remoteservice.rest.identity.RestID; +import org.eclipse.ecf.remoteservice.rest.identity.RestNamespace; + +/** + * This class is omnly used for creating instances of {@link RestContainer}. + */ +public class RestContainerInstantiatior implements IContainerInstantiator { + + public IContainer createInstance(ContainerTypeDescription description, Object[] parameters) throws ContainerCreateException { + if(!description.getName().equals(RestContainer.NAME)) + throw new ContainerCreateException(); + if(parameters == null) + throw new ContainerCreateException(); + if(parameters.length > 0 && (parameters[0] instanceof URL)) { + URL baseUrl = (URL) parameters[0]; + RestNamespace namespace = new RestNamespace(RestNamespace.NAME, description.getDescription()); + RestID id = new RestID(namespace, baseUrl); + return new RestContainer(id); + } + if(parameters.length > 0 && (parameters[0] instanceof ID)) { + return new RestContainer((ID) parameters[0]); + } + throw new ContainerCreateException(); + + } + + public String[] getSupportedAdapterTypes(ContainerTypeDescription description) { + + return null; + } + + public String[] getSupportedIntents(ContainerTypeDescription description) { + return null; + } + + public Class[][] getSupportedParameterTypes(ContainerTypeDescription description) { + if(!description.getName().equals(RestContainer.NAME)) + throw new IllegalArgumentException("Description must be "+RestContainer.NAME); + return new Class[][]{{ URL.class }}; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestService.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestService.java new file mode 100644 index 000000000..8428ae155 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestService.java @@ -0,0 +1,409 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.DeleteMethod; +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.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.security.Callback; +import org.eclipse.ecf.core.security.CallbackHandler; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.core.security.NameCallback; +import org.eclipse.ecf.core.security.ObjectCallback; +import org.eclipse.ecf.core.security.UnsupportedCallbackException; +import org.eclipse.ecf.core.util.ECFException; +import org.eclipse.ecf.internal.remoteservice.rest.ResourceRepresentationFactory; +import org.eclipse.ecf.remoteservice.IRemoteCall; +import org.eclipse.ecf.remoteservice.IRemoteCallListener; +import org.eclipse.ecf.remoteservice.IRemoteService; +import org.eclipse.ecf.remoteservice.IRemoteServiceReference; +import org.eclipse.ecf.remoteservice.events.IRemoteCallCompleteEvent; +import org.eclipse.ecf.remoteservice.events.IRemoteCallStartEvent; +import org.eclipse.ecf.remoteservice.rest.identity.RestID; +import org.eclipse.equinox.concurrent.future.AbstractExecutor; +import org.eclipse.equinox.concurrent.future.IFuture; +import org.eclipse.equinox.concurrent.future.IProgressRunnable; +import org.eclipse.equinox.concurrent.future.ThreadsExecutor; + +/** + * This class represents a REST service from the client side of view. So a RESTful + * web service can be accessed via the methods provided by this class. Mostly the + * methods are inherited from {@link IRemoteService}. + */ +public class RestService implements IRemoteService { + + /** + * inner class implementing the asynchronous result object. This + * implementation also provides the calling infrastructure. + */ + private class AsyncResult extends Thread { + + // the result of the call. + Object result; + + // the exception, if any happened during the call. + Throwable exception; + + // the remote call object. + IRestCall call; + + // the callback listener, if provided. + private IRemoteCallListener listener; + + // constructor + AsyncResult(final IRestCall call, final IRemoteCallListener listener) { + this.call = call; + this.listener = listener; + } + + // the call happens here. + public void run() { + Object r = null; + Throwable e = null; + + final long reqID = getNextID(); + + if (listener != null) { + listener.handleEvent(new IRemoteCallStartEvent() { + public IRemoteCall getCall() { + // TODO: should return the remoteCall + return null; + } + + public IRemoteServiceReference getReference() { + return reference; + } + + public long getRequestId() { + return reqID; + } + }); + } + + try { + r = callSync(call); + } catch (Throwable t) { + e = t; + } + + synchronized (AsyncResult.this) { + result = r; + exception = e; + AsyncResult.this.notify(); + } + + if (listener != null) { + listener.handleEvent(new IRemoteCallCompleteEvent() { + + public Throwable getException() { + return exception; + } + + public Object getResponse() { + return result; + } + + public boolean hadException() { + return exception != null; + } + + public long getRequestId() { + return reqID; + } + }); + } + } + } + + private int nextID; + private IRemoteServiceReference reference; + private Object proxy; + + public RestService(IRemoteServiceReference reference) { + this.reference = reference; + } + + public RestService(Object proxy) { + this.proxy = proxy; + reference = null; + } + + public RestService() { + reference = null; + } + + /** + * get the next call id. + * + * @return the next call id. + */ + synchronized long getNextID() { + return nextID++; + } + + public void callAsync(IRemoteCall call, IRemoteCallListener listener) { + callAsync(lookupRestCall(call), listener); + } + + public void callAsync(IRestCall restCall, IRemoteCallListener listener) { + new AsyncResult(restCall, listener).start(); + } + + public IFuture callAsync(final IRemoteCall call) { + return callAsync(lookupRestCall(call)); + } + + public IFuture callAsync(final IRestCall call) { + final AbstractExecutor executor = new ThreadsExecutor(); + return executor.execute(new IProgressRunnable() { + public Object run(IProgressMonitor monitor) throws Exception { + return callSync(call); + } + }, null); + } + + public Object callSync(IRemoteCall call) throws ECFException { + return callSync(lookupRestCall(call)); + } + + public Object callSync(IRestCall call) throws ECFException { + if(call == null) + throw new ECFException("no IRestCall found for IRemoteCall"); + return callHttpMethod(call); + } + + private IRestCall lookupRestCall(IRemoteCall call) { + if(reference instanceof RestServiceReference) { + RestServiceReference ref = (RestServiceReference) reference; + RestContainer container = ref.getContainer(); + return container.lookupRestCall(call); + } + return null; + } + + /** + * Calls the Rest service with given URL of IRestCall. The returned value is + * the response body as an InputStream. + * + * @param restCall + * The Rest Service to call represented by an IRestCall object + * @return The InputStream of the response body or null if an + * error occurs. + */ + public Object callHttpMethod(final IRestCall restCall) throws ECFException { + // call the method + HttpClient httpClient = new HttpClient(); + String url = handleURI(restCall.getURI().toString()); + HttpMethod httpMethod = createHttpMethod(restCall, url); + // add additional request headers + handleRequestHeaders(httpMethod, restCall.getRequestHeaders()); + // handle authentication + handleAuthentication(httpClient, httpMethod); + Object response = null; + // needed because a resource can link to another resource + httpClient.getParams().setParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, new Boolean(true)); + // execute method + try { + int responseCode = httpClient.executeMethod(httpMethod); + if(responseCode == HttpStatus.SC_OK) { + response = ResourceRepresentationFactory.getDefault() + .createResourceRepresentation(httpMethod, restCall); + if(proxy != null && proxy instanceof IRestResponseProcessor) + ((IRestResponseProcessor) proxy).processResource(response); + } else + throw new ECFException("Service returned status code: " + responseCode); + } catch (HttpException e) { + throw new ECFException(e); + } catch (IOException e) { + throw new ECFException(e); + } catch (ParseException e) { + throw new ECFException(e); + } + return response; + } + + protected void handleRequestHeaders(HttpMethod httpMethod, Map requestHeaders) { + if(requestHeaders != null) { + Set keySet = requestHeaders.keySet(); + Object[] headers = keySet.toArray(); + for(int i = 0; i < headers.length; i++) { + String key = (String) headers[i]; + String value = (String) requestHeaders.get(key); + httpMethod.addRequestHeader(key, value); + } + } + } + + protected HttpMethod createHttpMethod(IRestCall restCall, String url) throws ECFException { + HttpMethod httpMethod = null; + String method = restCall.getMethod(); + if(method.equals(IRestCall.HTTP_GET)) { + httpMethod = new GetMethod(url); + addGetParams(httpMethod, restCall.getParameters()); + } else if(method.equals(IRestCall.HTTP_POST)) { + httpMethod = new PostMethod(url); + addPostParams(httpMethod, restCall.getParameters(), restCall + .getRequestEntity()); + } else if(method.equals(IRestCall.HTTP_PUT)) { + httpMethod = new PutMethod(url); + addPutRequestBody(restCall, httpMethod); + } else if(method.equals(IRestCall.HTTP_DELETE)) { + httpMethod = new DeleteMethod(url); + } else { + throw new ECFException("HTTP method not supported"); + } + return httpMethod; + } + + protected void addPutRequestBody(IRestCall restCall, HttpMethod httpMethod) throws ECFException { + PutMethod putMethod = (PutMethod) httpMethod; + if(restCall.getParameters()[0] instanceof String) { + String body = (String) restCall.getParameters()[0]; + RequestEntity entity; + try { + entity = new StringRequestEntity(body, null, null); + } catch (UnsupportedEncodingException e) { + throw new ECFException( + "An error occured while creating the request entity", e); + } + putMethod.setRequestEntity(entity); + } else { + throw new ECFException( + "For put the first Parameter must be a String"); + } + } + + protected void addPostParams(HttpMethod httpMethod, Object[] restParams, RequestEntity requestEntity) { + PostMethod postMethod = (PostMethod) httpMethod; + // query parameters exclude a request entity + if(restParams != null && restParams.length > 0) { + postMethod.addParameters(toNameValuePairs(restParams)); + } else if(requestEntity != null) { + postMethod.setRequestEntity(requestEntity); + } + } + + protected void addGetParams(HttpMethod httpMethod, Object[] restParams) { + if(restParams != null) { + httpMethod.setQueryString(toNameValuePairs(restParams)); + } + } + + private NameValuePair[] toNameValuePairs(Object[] restParams) { + List nameValueList = new ArrayList(restParams.length); + for(int i = 0; i < restParams.length; i++) { + if(restParams[i] instanceof String) { + String param = (String) restParams[i]; + int indexOfEquals = param.indexOf('='); + String key = param.substring(0, indexOfEquals); + String value = param.substring(indexOfEquals + 1, param + .length()); + nameValueList.add(new NameValuePair(key, value)); + } + } + return (NameValuePair[]) nameValueList.toArray(new NameValuePair[nameValueList.size()]); + } + + protected void handleAuthentication(HttpClient httpClient, HttpMethod method) { + if(reference instanceof RestServiceReference) { + RestServiceReference ref = (RestServiceReference) reference; + RestContainer container = ref.getContainer(); + IConnectContext connectContext = container.getConnectContext(); + if(connectContext != null) { + NameCallback nameCallback = new NameCallback(""); + ObjectCallback passwordCallback = new ObjectCallback(); + Callback[] callbacks = new Callback[] { nameCallback, + passwordCallback }; + CallbackHandler callbackHandler = connectContext + .getCallbackHandler(); + if(callbackHandler == null) + return; + try { + callbackHandler.handle(callbacks); + String username = nameCallback.getName(); + String password = (String) passwordCallback.getObject(); + AuthScope authscope = new AuthScope(null, -1); + Credentials credentials = new UsernamePasswordCredentials( + username, password); + httpClient.getState() + .setCredentials(authscope, credentials); + method.setDoAuthentication(true); + } catch (IOException e) { + e.printStackTrace(); + } catch (UnsupportedCallbackException e) { + e.printStackTrace(); + } + + } + } + } + + private String handleURI(String uri) { + if(uri.indexOf("http://") > -1) + return uri; + ID containerID = reference.getContainerID(); + + if(containerID instanceof RestID) { + RestID id = (RestID) containerID; + URL baseURL = id.getBaseURL(); + String baseUrlString = baseURL.toExternalForm(); + int length = baseUrlString.length(); + char[] lastChar = new char[1]; + baseUrlString.getChars(length - 1, length, lastChar, 0); + char[] firstMethodChar = new char[1]; + uri.getChars(0, 1, firstMethodChar, 0); + if((lastChar[0] == '/' && firstMethodChar[0] != '/') + || (lastChar[0] != '/' && firstMethodChar[0] == '/')) + return baseUrlString + uri; + else if(lastChar[0] == '/' && firstMethodChar[0] == '/') { + String tempurl = baseUrlString.substring(0, length - 1); + return tempurl + uri; + } else if(lastChar[0] != '/' && firstMethodChar[0] != '/') + return baseUrlString + "/" + uri; + } + return null; + } + + public Object getProxy() throws ECFException { + return proxy; + } + + void setReference(IRemoteServiceReference reference) { + this.reference = reference; + } + + public void fireAsync(IRemoteCall call) throws ECFException { + callAsync(call); + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceReference.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceReference.java new file mode 100644 index 000000000..fa52ceed0 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceReference.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.remoteservice.IRemoteServiceID; +import org.eclipse.ecf.remoteservice.IRemoteServiceReference; + +/** + * Objects of this class represents the reference for a {@link RestService}. + */ +public class RestServiceReference implements IRemoteServiceReference { + + private RestServiceRegistration registration; + + public RestServiceReference(RestServiceRegistration restServiceRegistration) { + registration = restServiceRegistration; + } + + RestContainer getContainer(){ + return registration.getContainer(); + } + + public ID getContainerID() { + return registration.getContainerID(); + } + + public IRemoteServiceID getID() { + return registration.getID(); + } + + public Object getProperty(String key) { + return registration.getProperty(key); + } + + public String[] getPropertyKeys() { + return registration.getPropertyKeys(); + } + + public boolean isActive() { + return registration != null; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceRegistration.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceRegistration.java new file mode 100644 index 000000000..e99decdef --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/RestServiceRegistration.java @@ -0,0 +1,106 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest; + +import java.net.URL; +import java.util.Dictionary; +import java.util.Enumeration; + +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.identity.IDFactory; +import org.eclipse.ecf.core.identity.Namespace; +import org.eclipse.ecf.internal.remoteservice.rest.RestServiceRegistry; +import org.eclipse.ecf.remoteservice.IRemoteService; +import org.eclipse.ecf.remoteservice.IRemoteServiceID; +import org.eclipse.ecf.remoteservice.IRemoteServiceReference; +import org.eclipse.ecf.remoteservice.IRemoteServiceRegistration; +import org.eclipse.ecf.remoteservice.rest.identity.RestID; +import org.eclipse.ecf.remoteservice.rest.identity.RestNamespace; + +/** + * This class acts as the registration for {@link RestService}s. + */ +public class RestServiceRegistration implements IRemoteServiceRegistration { + + private IRemoteServiceReference reference; + private Object service; + private String[] clazzes; + private Dictionary properties; + private ID containerId; + private RestServiceRegistry registry; + private IRemoteServiceID serviceID; + + public RestServiceRegistration(String[] clazzes, Object service, Dictionary properties, RestServiceRegistry registry) { + this.service = service; + this.clazzes = clazzes; + this.properties = properties; + containerId = registry.getContainerId(); + reference = new RestServiceReference(this); + this.registry = registry; + registry.registerRegistration(this); + } + + public String[] getClazzes() { + return clazzes; + } + + public ID getContainerID() { + return containerId; + } + + public IRemoteServiceID getID() { + if(serviceID == null){ + Namespace namespace = IDFactory.getDefault().getNamespaceByName(RestNamespace.NAME); + URL baseURL = ((RestID)containerId).getBaseURL(); + serviceID = (IRemoteServiceID) IDFactory.getDefault().createID(namespace, new Object[] {baseURL, containerId, new Long(registry.getNextServiceId())}); + } + return serviceID; + } + + public Object getProperty(String key) { + return properties.get(key); + } + + public String[] getPropertyKeys() { + int length = properties.size(); + Enumeration keys = properties.keys(); + String[] result = new String[length]; + int i = 0; + while (keys.hasMoreElements()) { + Object element = keys.nextElement(); + if(element instanceof String){ + result[i] = (String) element; + i++; + } + } + return result; + } + + public IRemoteService getService() { + return (IRemoteService)service; + } + + public IRemoteServiceReference getReference() { + return reference; + } + + public void setProperties(Dictionary properties) { + this.properties = properties; + } + + public void unregister() { + registry.unregisterRegistration(this); + } + + RestContainer getContainer() { + return registry.getContainer(); + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestID.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestID.java new file mode 100644 index 000000000..68aa7c564 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestID.java @@ -0,0 +1,96 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.identity; + +import java.net.URL; + +import org.eclipse.ecf.core.identity.BaseID; +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.identity.Namespace; +import org.eclipse.ecf.remoteservice.IRemoteServiceID; +import org.eclipse.ecf.remoteservice.rest.RestContainer; + +/** + * An ECF ID to instatiate a {@link RestContainer}. + */ +public class RestID extends BaseID implements IRemoteServiceID { + + private static final long serialVersionUID = -7725015677223101132L; + URL baseUrl; + private Long serviceID; + private ID containerId; + + /** + * Contructor to create a RestID with a {@link Namespace}. + * + * @param namespace Must be an instance of {@link RestNamespace}. + * @param baseURL an URL which will be associated with this ID to call REST services, + * i.e. http://twitter.com for Twitter services. + */ + public RestID( Namespace namespace, URL baseURL ) { + super(namespace); + this.baseUrl = baseURL; + } + + /** + * @see RestID#RestID(Namespace, URL). + * + * @param serviceID the service ID to use. + */ + public RestID(RestNamespace restNamespace, URL url, Long serviceID) { + this(restNamespace, url); + this.serviceID = serviceID; + } + + /** + * @see RestID#RestID(RestNamespace, URL, Long). + * + * @param containerId the ID of the associated container. + */ + public RestID(RestNamespace restNamespace, URL url, ID containerId, Long serviceID) { + this(restNamespace, url, serviceID); + this.containerId = containerId; + } + + public int namespaceCompareTo(BaseID o) { + return this.baseUrl.toExternalForm().compareTo(((RestID) o).toExternalForm()); + } + + public boolean namespaceEquals(BaseID o) { + return this.baseUrl.equals(((RestID) o).baseUrl); + } + + public String namespaceGetName() { + return this.baseUrl.toExternalForm(); + } + + public int namespaceHashCode() { + return this.baseUrl.hashCode(); + } + + public URL getBaseURL() { + return baseUrl; + } + + public ID getContainerID() { + return containerId; + } + + public long getContainerRelativeID() { + if(serviceID == null) + return 0; + return serviceID.longValue(); + } + + public void setBaseUrl(URL baseUrl) { + this.baseUrl = baseUrl; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestNamespace.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestNamespace.java new file mode 100644 index 000000000..56af8a1ca --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/identity/RestNamespace.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.identity; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.ecf.core.identity.ID; +import org.eclipse.ecf.core.identity.IDCreateException; +import org.eclipse.ecf.core.identity.Namespace; +import org.eclipse.ecf.remoteservice.rest.RestContainer; + +/** + * This class represents a {@link Namespace} for {@link RestContainer}s. + */ +public class RestNamespace extends Namespace { + + private static final long serialVersionUID = -398861350452016954L; + + /** + * The name of this namespace. + */ + public static final String NAME = "ecf.rest.namespace"; + + /** + * The scheme of this namespace. + */ + public static final String SCHEME = "rest"; + + + public RestNamespace() { + } + + public RestNamespace(String name, String desc) { + super(name, desc); + } + + /** + * Creates an instance of an {@link RestID}. The parameters must contain specific information. + * + * First it should contain a String which represents the {@link RestID#baseUrl}. + * Additional it could contain a ServiceId and/or a ContainerId. + * + * @param parameters a collection of attributes to call the right constructor on {@link RestID}. + * @return an instance of {@link RestID}. Will not be null. + */ + public ID createInstance(Object[] parameters) throws IDCreateException { + URL url = null; + if(parameters[0] instanceof String) { + try { + url = new URL((String) parameters[0]); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e.getLocalizedMessage()); + } + } else if((parameters[0] instanceof URL)) { + url = (URL) parameters[0]; + } else + throw new IllegalArgumentException("the first parameter must be transformable to an URL"); + if(parameters.length == 2 && parameters[1] instanceof Long) + return new RestID(this, url, (Long)parameters[1] ); + if( parameters.length == 3 && parameters[1] instanceof ID && parameters[2] instanceof Long) + return new RestID(this, url, (ID)parameters[1], (Long)parameters[2] ); + return new RestID(this, url ); + } + + public String getScheme() { + return SCHEME; + } + + public Class[][] getSupportedParameterTypes() { + return new Class[][] { { URL.class } }; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/IRestResource.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/IRestResource.java new file mode 100644 index 000000000..92068dc70 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/IRestResource.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.resource; + +import java.text.ParseException; + +/** + * This interface can be used to register services for the resource representation creation process. + * A sample implementation can be found on {@link XMLResource} for XML. + */ +public interface IRestResource { + + /** + * Returns the identifier of this resource. This is used to check which resource + * should be used to parse a response from a IRestCall. Therefore the result from + * {@link IRestCall#getEstimatedIdentifier} will be compared with the result of this method. + * + * @return the identifier for this resource. Cannot be null + */ + public String getIdentifier(); + + /** + * Parse a REST response to a given format i.e. XML or JSON. Implementations + * can be registered with a normal OSGi service. + * + * @param responseBody the string representation from the response. + * @return the parsed response. Can be null + */ + public Object createRepresentation(String responseBody) throws ParseException; + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/XMLResource.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/XMLResource.java new file mode 100644 index 000000000..9d00b9356 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/resource/XMLResource.java @@ -0,0 +1,55 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.resource; + +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.ecf.internal.remoteservice.rest.Activator; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * This class is a sample implementation of {@link IRestResource}. This will be used + * to create XML Resource representations and will be registered when the API is + * started, {@link Activator#start(org.osgi.framework.BundleContext)}. + */ +public class XMLResource implements IRestResource { + + public String getIdentifier() { + return "ecf.rest.resource.xml"; + } + + public Object createRepresentation(String responseBody) throws IllegalArgumentException { + DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); + String errorMsg = "Response can't be parsed, reason: "; + try { + DocumentBuilder builder = documentFactory.newDocumentBuilder(); + InputSource src = new InputSource(new StringReader(responseBody)); + Document dom = builder.parse(src); + return dom; + } catch (ParserConfigurationException e) { + throw new IllegalArgumentException(errorMsg + e.getMessage()); + } catch (SAXException e) { + throw new IllegalArgumentException(errorMsg + e.getMessage()); + } catch (IOException e) { + throw new IllegalArgumentException(errorMsg + e.getMessage()); + } + + + + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/DeleteRestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/DeleteRestCall.java new file mode 100644 index 000000000..bb2785f13 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/DeleteRestCall.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Implementation for http DELETE. + */ +public class DeleteRestCall extends RestCall { + + public DeleteRestCall(URI uri, String resourceIdentifier, Object[] params, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, params, null, requestHeaders, timeout); + } + + public DeleteRestCall(URI uri, String resourceIdentifier, RequestEntity requestEntity, Map requestHeaders, + long timeout) { + this(uri, resourceIdentifier, null, requestEntity, requestHeaders, timeout); + } + + protected DeleteRestCall(URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, + Map requestHeaders, long timeout) { + super(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + } + + public DeleteRestCall(URI uri, String resourceIdentifier, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, null, null, requestHeaders, timeout); + } + + public String getMethod() { + return IRestCall.HTTP_DELETE; + } +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/GetRestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/GetRestCall.java new file mode 100644 index 000000000..521f84b9c --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/GetRestCall.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Implementation for http GET. + */ +public class GetRestCall extends RestCall { + + public GetRestCall(URI uri, String resourceIdentifier, Object[] params, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, params, null, requestHeaders, timeout); + } + + public GetRestCall(URI uri, String resourceIdentifier, RequestEntity requestEntity, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, null, requestEntity, requestHeaders, timeout); + } + + protected GetRestCall(URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, + Map requestHeaders, long timeout) { + super(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + } + + public GetRestCall(URI uri, String resourceIdentifier, Map requestHeaders, + long timeout) { + this(uri, resourceIdentifier, null, null, requestHeaders, timeout); + } + + + public String getMethod() { + return IRestCall.HTTP_GET; + } + + public RequestEntity getRequestEntity() { + // GET calls may not have request entities + return null; + } +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PostRestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PostRestCall.java new file mode 100644 index 000000000..809b0e8f6 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PostRestCall.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Implementation for http POST. + */ +public class PostRestCall extends RestCall { + + protected PostRestCall(URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, + Map requestHeaders, long timeout) { + super(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + } + + public PostRestCall(URI uri, String resourceIdentifier, Object[] params, Map requestHeaders, long timeout) { + super(uri, resourceIdentifier, params, null, requestHeaders, timeout); + } + + public PostRestCall(URI uri, String resourceIdentifier, RequestEntity requestEntity, Map requestHeaders, + long timeout) { + super(uri, resourceIdentifier, null, requestEntity, requestHeaders, timeout); + } + + public PostRestCall(URI uri, String resourceIdentifier, Map requestHeaders, + long timeout) { + this(uri, resourceIdentifier, null, null, requestHeaders, timeout); + } + + public String getMethod() { + return IRestCall.HTTP_POST; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PutRestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PutRestCall.java new file mode 100644 index 000000000..4d23941fb --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/PutRestCall.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Implementation for http PUT. + */ +public class PutRestCall extends RestCall { + + public PutRestCall(URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, + Map requestHeaders, long timeout) { + super(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + } + + public PutRestCall(URI uri, String resourceIdentifier, Map requestHeaders, Object[] params, long timeout) { + this(uri, resourceIdentifier, params, null, requestHeaders, timeout); + } + + public PutRestCall(URI uri, String resourceIdentifier, RequestEntity requestEntity, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, null, requestEntity, requestHeaders, timeout); + } + + public PutRestCall(URI uri, String resourceIdentifier, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, null, null, requestHeaders, timeout); + } + + public String getMethod() { + return IRestCall.HTTP_PUT; + } +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCall.java new file mode 100644 index 000000000..ee60b7d2a --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCall.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * This class acts as the super class for the service object {@link GetRestCall}, + * {@link PostRestCall}, {@link DeleteRestCall} and {@link PutRestCall}. Sub classes + * may override the methods from {@link IRestCall}. + */ +public class RestCall implements IRestCall { + + private long timeout; + private Map requestHeaders; + private Object[] params; + private RequestEntity requestEntity; + private String resourceIdentifier; + private URI uri; + private String method; + + + protected RestCall(URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, Map requestHeaders, long timeout) { + this.uri = uri; + this.resourceIdentifier = resourceIdentifier; + this.params = params; + this.requestEntity = requestEntity; + this.requestHeaders = requestHeaders; + this.timeout = timeout; + } + + protected RestCall(String method, URI uri, String resourceIdentifier, Object[] params, RequestEntity requestEntity, Map requestHeaders, long timeout) { + this(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + this.method = method; + } + + public String getEstimatedResourceIdentifier() { + return resourceIdentifier; + } + + public String getMethod() { + return method; + } + + public Object[] getParameters() { + return params; + } + + public Map getRequestHeaders() { + return requestHeaders; + } + + public long getTimeout() { + return timeout; + } + + public URI getURI() { + return uri; + } + + public RequestEntity getRequestEntity() { + return requestEntity; + } + + +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCallFactory.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCallFactory.java new file mode 100644 index 000000000..8ef188660 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestCallFactory.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2009 EclipseSource 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: + * EclipseSource - initial API and implementation + *******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import java.net.URI; +import java.util.Map; + +import org.apache.commons.httpclient.methods.RequestEntity; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Factory to support the creation of {@link IRestCall} objects. + */ +public class RestCallFactory { + + /** + * Creates a specific {@link IRestCall}. + * + * @param method + * The type of the http method. GET, PUT, DELETE or POST. + * @param uri + * {@link IRestCall#getURI()} + * @param resourceIdentifier + * {@link IRestCall#getEstimatedResourceIdentifier()} + * @param params + * {@link IRestCall#getParameters()} + * @param requestHeaders + * {@link IRestCall#getRequestHeaders()} + * @param timeout + * {@link IRestCall#getTimeout()} + * + * @return {@link IRestCall} object filled with the overgiven params. Will + * not be null. + */ + public static IRestCall createRestCall(final String method, final URI uri, final String resourceIdentifier, + final Object[] params, final Map requestHeaders, final long timeout) { + + return createRestCall(method, uri, resourceIdentifier, params, null, requestHeaders, timeout); + } + + /** + * Creates a specific {@link IRestCall}. + * + * @param method + * The type of the http method. GET, PUT, DELETE or POST. + * @param uri + * {@link IRestCall#getURI()} + * @param resourceIdentifier + * {@link IRestCall#getEstimatedResourceIdentifier()} + * @param requestEntity + * {@link IRestCall#getRequestEntity()} + * @param requestHeaders + * {@link IRestCall#getRequestHeaders()} + * @param timeout + * {@link IRestCall#getTimeout()} + * + * @return {@link IRestCall} object filled with the overgiven params. Will + * not be null. + */ + public static IRestCall createRestCall(final String method, final URI uri, final String resourceIdentifier, + final RequestEntity requestEntity, final Map requestHeaders, final long timeout) { + return createRestCall(method, uri, resourceIdentifier, null, requestEntity, requestHeaders, timeout); + } + + /** + * Creates a specific {@link IRestCall}. + * + * @param method + * The type of the http method. GET, PUT, DELETE or POST. + * @param uri + * {@link IRestCall#getURI()} + * @param resourceIdentifier + * {@link IRestCall#getEstimatedResourceIdentifier()} + * @param requestHeaders + * {@link IRestCall#getRequestHeaders()} + * @param timeout + * {@link IRestCall#getTimeout()} + * + * @return {@link IRestCall} object filled with the overgiven params. Will + * not be null. + */ + public static IRestCall createRestCall(final String method, final URI uri, final String resourceIdentifier, + final Map requestHeaders, final long timeout) { + return createRestCall(method, uri, resourceIdentifier, null, null, requestHeaders, timeout); + } + + private static IRestCall createRestCall(final String method, final URI uri, final String resourceIdentifier, + final Object[] params, final RequestEntity requestEntity, final Map requestHeaders, final long timeout) { + + if (method.equals(IRestCall.HTTP_GET)) + return new GetRestCall(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + if (method.equals(IRestCall.HTTP_POST)) + return new PostRestCall(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + if (method.equals(IRestCall.HTTP_DELETE)) + return new DeleteRestCall(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + if (method.equals(IRestCall.HTTP_PUT)) + return new PutRestCall(uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + return new RestCall(method, uri, resourceIdentifier, params, requestEntity, requestHeaders, timeout); + } +} diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestRemoteCall.java b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestRemoteCall.java new file mode 100644 index 000000000..125dd58f8 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.remoteservice.rest/src/org/eclipse/ecf/remoteservice/rest/util/RestRemoteCall.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* Copyright (c) 2009 EclipseSource 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: +* EclipseSource - initial API and implementation +*******************************************************************************/ +package org.eclipse.ecf.remoteservice.rest.util; + +import org.eclipse.ecf.remoteservice.IRemoteCall; +import org.eclipse.ecf.remoteservice.rest.IRestCall; + +/** + * Service class for creating a {@link IRemoteCall}. This will be used to associate + * an {@link IRemoteCall} with an {@link IRestCall}. Therefore the {@link #getMethod()} + * method will used as key. + */ +public class RestRemoteCall implements IRemoteCall { + + private String key; + + public RestRemoteCall(String key) { + this.key = key; + } + + public String getMethod() { + return key; + } + + public Object[] getParameters() { + return null; + } + + public long getTimeout() { + return 0; + } +} -- cgit v1.2.3