diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/ODataClientBuilder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/ODataClientBuilder.java new file mode 100644 index 000000000..bd3cbdeae --- /dev/null +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/ODataClientBuilder.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.client.api; + +import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.format.ODataFormat; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + *

+ * Builder to create an ODataClient for the API client library. + * This builder is dependent that an according implementation for the ODataClient and or + * EdmEnabledODataClient is available in class path. + *

+ *

+ * This Builder should only be used in use cases were a direct access to the client-core + * library is not possible. + * If direct access is possible it is highly recommended to use the + * ODataClientFactory provided in the client-core library. + *

+ *

+ * By default the ODataClientBuilder use the default Olingo V4 client core implementations + * (org.apache.olingo.client.core.ODataClientImpl and + * org.apache.olingo.client.core.EdmEnabledODataClientImpl) which can be + * overwritten via the System properties ODATA_CLIENT_IMPL_SYS_PROPERTY + * and ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY. + *

+ */ +public final class ODataClientBuilder { + + private static final String ODATA_CLIENT_IMPL_CLASS = "org.apache.olingo.client.core.ODataClientImpl"; + private static final String ODATA_EDM_CLIENT_IMPL_CLASS = "org.apache.olingo.client.core.EdmEnabledODataClientImpl"; + public static final String ODATA_CLIENT_IMPL_SYS_PROPERTY = "ORG_APACHE_OLINGO_CLIENT_IMPL_FQN"; + public static final String ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY = "ORG_APACHE_OLINGO_EDM_CLIENT_IMPL_FQN"; + + /** + * Builder class + */ + public static class ClientBuilder { + private final String serviceRoot; + private Edm edm; + private String metadataETag; + + /** + * Create the builder for an EdmEnabledODataClient. + * + * @param serviceRoot service root to use + */ + public ClientBuilder(String serviceRoot) { + this.serviceRoot = serviceRoot; + } + + /** + * Set the edm to use for edm enabled client + * @param edm edm to use for edm enabled client + * @return current client builder + */ + public ClientBuilder edm(final Edm edm) { + this.edm = edm; + return this; + } + + /** + * Set the metadataETag to use for edm enabled client + * @param metadataETag edm to use for edm enabled client + * @return current client builder + */ + public ClientBuilder metadataETag(final String metadataETag) { + this.metadataETag = metadataETag; + return this; + } + + /** + * Create an new EdmEnabledODataClient based on via system property ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY + * class name or if not net the default ODATA_EDM_CLIENT_IMPL_CLASS set class + * with before set serviceRoot and optional edm and optinal metadataETag. + * @return new created ODataClient + */ + public EdmEnabledODataClient createClient() { + return ODataClientBuilder.createEdmEnabledClient(serviceRoot, edm, metadataETag); + } + } + + /** + * Create an new ODataClient based on via system property ODATA_CLIENT_IMPL_SYS_PROPERTY + * class name or if not net the default ODATA_CLIENT_IMPL_CLASS set class. + * @return create ODataClient + */ + public static ODataClient createClient() { + String clientImplClassName = System.getProperty(ODATA_CLIENT_IMPL_SYS_PROPERTY); + if(clientImplClassName == null) { + clientImplClassName = ODATA_CLIENT_IMPL_CLASS; + } + return loadClass(ODataClient.class, clientImplClassName); + } + + /** + * Initiate the builder for an EdmEnabledODataClient. + * + * @param serviceRoot service root to use + * @return initiated client builder + */ + public static ClientBuilder with(String serviceRoot) { + return new ClientBuilder(serviceRoot); + } + + + /** + * Create an new EdmEnabledODataClient based on via system property ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY + * class name or if not net the default ODATA_EDM_CLIENT_IMPL_CLASS set class. + * @param serviceRoot used service root + * @param edm used Edm + * @param metadataETag used metadataETag + * @return create ODataClient + */ + private static EdmEnabledODataClient createEdmEnabledClient( + final String serviceRoot, final Edm edm, final String metadataETag) { + + String clientImplClassName = System.getProperty(ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY); + if(clientImplClassName == null) { + clientImplClassName = ODATA_EDM_CLIENT_IMPL_CLASS; + } + final EdmEnabledODataClient instance = + loadClass(EdmEnabledODataClient.class, clientImplClassName, + new Class[] { String.class, Edm.class, String.class }, + new Object[] { serviceRoot, edm, metadataETag }); + instance.getConfiguration().setDefaultPubFormat(ODataFormat.JSON); + return instance; + } + + private static T loadClass(Class typeOfClass, String className) { + return loadClass(typeOfClass, className, null, null); + } + + private static T loadClass(Class typeOfClass, String className, + Class[] ctorParameterClasses, + Object[] ctorParameters) { + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); + if (ctorParameters == null || ctorParameterClasses == null) { + return typeOfClass.cast(clazz.newInstance()); + } + Constructor ctor = clazz.getConstructor(ctorParameterClasses); + return typeOfClass.cast(ctor.newInstance(ctorParameters)); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Requested class '" + className + "' could not be loaded.", e); + } catch (InstantiationException e) { + throw new RuntimeException("Requested class '" + className + "' could not be loaded.", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Requested class '" + className + "' could not be loaded.", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Requested class '" + className + "' could not be loaded.", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Requested class '" + className + "' could not be loaded.", e); + } + } +} diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/ODataClientBuilderTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/ODataClientBuilderTest.java new file mode 100644 index 000000000..164946d66 --- /dev/null +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/ODataClientBuilderTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.client.core; + +import org.apache.olingo.client.api.EdmEnabledODataClient; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.ODataClientBuilder; +import org.apache.olingo.commons.api.edm.Edm; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * The ODataClientBuilderTest must be done in the core library because for test reason it is necessary + * that the ODataClientImpl and EdmEnabledODataClientImpl classes are in the class + * path. + * Furthermore the client-core must have the dependency to the client-api + * so that is ensured that the ODataClientBuilder is available. + */ +public class ODataClientBuilderTest { + + @Test + public void testDefault() { + ODataClient client = ODataClientBuilder.createClient(); + assertNotNull(client); + assertTrue(client instanceof ODataClientImpl); + assertFalse(client instanceof EdmEnabledODataClientImpl); + + EdmEnabledODataClient edmClient = ODataClientBuilder.with("http://serviceRoot").createClient(); + assertNotNull(client); + assertTrue(edmClient instanceof ODataClientImpl); + assertTrue(edmClient instanceof EdmEnabledODataClientImpl); + } + + @Test + public void testSystemProperty() { + //CHECKSTYLE:OFF + System.setProperty(ODataClientBuilder.ODATA_CLIENT_IMPL_SYS_PROPERTY, MyODataClient.class.getName()); + ODataClient client = ODataClientBuilder.createClient(); + assertNotNull(client); + assertTrue(client instanceof ODataClientImpl); + assertFalse(client instanceof EdmEnabledODataClientImpl); + assertTrue(client instanceof MyODataClient); + + System.setProperty(ODataClientBuilder.ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY, MyEdmODataClient.class.getName()); + EdmEnabledODataClient edmClient = ODataClientBuilder.with("http://serviceRoot").createClient(); + assertNotNull(client); + assertTrue(edmClient instanceof ODataClientImpl); + assertTrue(edmClient instanceof EdmEnabledODataClientImpl); + assertTrue(edmClient instanceof MyEdmODataClient); + assertNull(edmClient.getCachedEdm()); + + Edm edm = Mockito.mock(Edm.class); + edmClient = ODataClientBuilder.with("http://serviceRoot").edm(edm).metadataETag("ETAG").createClient(); + assertNotNull(client); + assertTrue(edmClient instanceof ODataClientImpl); + assertTrue(edmClient instanceof EdmEnabledODataClientImpl); + assertTrue(edmClient instanceof MyEdmODataClient); + assertNotNull(edmClient.getCachedEdm()); + + System.clearProperty(ODataClientBuilder.ODATA_CLIENT_IMPL_SYS_PROPERTY); + System.clearProperty(ODataClientBuilder.ODATA_EMD_CLIENT_IMPL_SYS_PROPERTY); + //CHECKSTYLE:ON + } + + public static class MyODataClient extends ODataClientImpl { + } + + public static class MyEdmODataClient extends EdmEnabledODataClientImpl { + private Edm myEdm; + public MyEdmODataClient(String serviceRoot, Edm edm, String metadataETag) { + super(serviceRoot, edm, metadataETag); + this.myEdm = edm; + } + + @Override + public Edm getCachedEdm() { + return myEdm; + } + } +}