package org.odata4j.consumer; import java.lang.reflect.Method; import java.util.Arrays; import org.odata4j.consumer.behaviors.OClientBehavior; import org.odata4j.consumer.behaviors.OClientBehaviors; import org.odata4j.core.Throwables; import org.odata4j.format.FormatType; /** * <code>ODataConsumer</code> is the client-side interface to an OData service. * * <p>Use {@link ODataConsumers#create(String)} or {@link ODataConsumers#newBuilder(String)} to connect to an existing OData service.</p> * * <p>If found, the Jersey implementation of {@link ODataConsumer} is used by default. Otherwise * the CXF implementation is loaded. If specified the {@code odata4j.consumerimpl} system property * overrules this default behavior. Its value can be either one of the predefined constants {@code * jersey} and {@code cxf} or any other concrete full class name, e.g. {@code * foo.bar.OtherConsumer}. By convention, {@code foo.bar.OtherConsumer} must implement a factory * method {@code public static ODataConsumer.Builder newBuilder(String serviceRootUri)}.</p> * * @see ODataConsumer */ public class ODataConsumers { public static final String CONSUMERIMPL_PROPERTY = "odata4j.consumerimpl"; public static final String JERSEY_CONSUMERIMPL = "jersey"; public static final String CXF_CONSUMERIMPL = "cxf"; public static final String JERSEY_CONSUMER_CLASSNAME = "org.odata4j.examples.jersey.consumer.ODataJerseyConsumer"; public static final String CXF_CONSUMER_CLASSNAME = "org.odata4j.examples.cxf.consumer.ODataCxfConsumer"; private static ClassLoader classLoader; /** * Builder for {@link ODataConsumer} objects. */ public static class Builder implements ODataConsumer.Builder { private ODataConsumer.Builder consumerBuilder; private Builder(String serviceRootUri) { if (classLoader == null) classLoader = getClass().getClassLoader(); try { String[] classNames = getClassNames(); Class<?> consumerClass = getConsumerClass(classNames); Method newBuilderMethod = consumerClass.getDeclaredMethod("newBuilder", String.class); consumerBuilder = (ODataConsumer.Builder) newBuilderMethod.invoke(consumerClass, serviceRootUri); } catch (Exception e) { Throwables.propagate(e); } } private String[] getClassNames() { String impl = System.getProperty(CONSUMERIMPL_PROPERTY); if (JERSEY_CONSUMERIMPL.equalsIgnoreCase(impl)) return new String[] { JERSEY_CONSUMER_CLASSNAME }; else if (CXF_CONSUMERIMPL.equalsIgnoreCase(impl)) return new String[] { CXF_CONSUMER_CLASSNAME }; else if (impl != null && impl.length() > 0) return new String[] { impl }; else // default return new String[] { JERSEY_CONSUMER_CLASSNAME, CXF_CONSUMER_CLASSNAME }; } private Class<?> getConsumerClass(String[] classNames) throws ClassNotFoundException { for (String className : classNames) { try { Class<?> consumerClass = Class.forName(className, true, classLoader); if (consumerClass != null) return consumerClass; } catch (ClassNotFoundException e) { // will be re-thrown at the end of this method (if no class could be loaded) } } throw new ClassNotFoundException("Unable to load ODataConsumer implementation. The following class(es) could not be found: " + Arrays.toString(classNames)); } @Override public ODataConsumer.Builder setFormatType(FormatType formatType) { return consumerBuilder.setFormatType(formatType); } @Override public ODataConsumer.Builder setClientBehaviors(OClientBehavior... clientBehaviors) { return consumerBuilder.setClientBehaviors(clientBehaviors); } @Override public ODataConsumer build() { return consumerBuilder.build(); } } /** * Constructs a new builder for an {@link ODataConsumer} object. * * @param serviceRootUri the OData service root uri */ public static ODataConsumer.Builder newBuilder(String serviceRootUri) { return new Builder(serviceRootUri); } /** * Creates a new {@link ODataConsumer} for the given OData service root uri. * * <p>Wrapper for {@code ODataConsumers.newBuilder(serviceRootUri).build()}. * * @param serviceRootUri the OData service root uri * @return a new OData consumer */ public static ODataConsumer create(String serviceRootUri) { return ODataConsumers.newBuilder(serviceRootUri).build(); } /** * Creates a new OData consumer for the Azure Table Storage service. * * @param account azure account key * @param key azure secret key * @return a new OData consumer for the Azure Table Storage service * @see <a href="http://msdn.microsoft.com/en-us/library/dd179423.aspx">[msdn] Table Service API</a> */ public static ODataConsumer azureTables(String account, String key) { String url = "http://" + account + ".table.core.windows.net/"; return ODataConsumers.newBuilder(url).setClientBehaviors(OClientBehaviors.azureTables(account, key)).build(); } /** * Creates a new OData consumer for the (now obsolete?) "dallas" service. * * @param serviceRootUri the service uri * @param accountKey dallas account key * @param uniqueUserId dallas user id * @return a new OData consumer for the (now obsolete?) "dallas" service */ public static ODataConsumer dallas(String serviceRootUri, String accountKey, String uniqueUserId) { // CTP2 //OClientBehavior dallasAuth = new DallasCtp2AuthenticationBehavior(accountKey, uniqueUserId); //OClientBehavior paging = new OldStylePagingBehavior(50, 1); //return ODataConsumer.create(serviceRootUri, dallasAuth, paging); // CTP3 OClientBehavior basicAuth = OClientBehaviors.basicAuth("accountKey", accountKey); return ODataConsumers.newBuilder(serviceRootUri).setClientBehaviors(basicAuth).build(); } /** * Creates a new OData consumer for the Windows Azure DataMarket service. * * @param serviceRootUri the service uri * @param accountKey account key for basic authentication * @return a new OData consumer for the Windows Azure DataMarket service */ public static ODataConsumer dataMarket(String serviceRootUri, String accountKey) { OClientBehavior basicAuth = OClientBehaviors.basicAuth("accountKey", accountKey); return ODataConsumers.newBuilder(serviceRootUri).setClientBehaviors(basicAuth).build(); } }