You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by il...@apache.org on 2014/07/21 13:33:52 UTC

git commit: [OLINGO-362] Now supporting refresh token

Repository: olingo-odata4
Updated Branches:
  refs/heads/master df7cba690 -> c155238d9


[OLINGO-362] Now supporting refresh token


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/c155238d
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/c155238d
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/c155238d

Branch: refs/heads/master
Commit: c155238d9cce9d1a2b170ac8a898b126b09c0f9a
Parents: df7cba6
Author: Francesco Chicchiriccò <--global>
Authored: Mon Jul 21 13:33:34 2014 +0200
Committer: Francesco Chicchiriccò <--global>
Committed: Mon Jul 21 13:33:34 2014 +0200

----------------------------------------------------------------------
 .../java/org/apache/olingo/fit/V4OAuth2.java    |   4 -
 .../olingo/fit/rest/OAuth2InInterceptor.java    |  98 ----------
 .../apache/olingo/fit/rest/OAuth2Provider.java  |   2 -
 .../olingo/fit/rest/OAuth2RequestFilter.java    |  38 ++++
 .../main/webapp/WEB-INF/applicationContext.xml  |   5 +-
 .../olingo/fit/CXFOAuth2HttpClientFactory.java  | 181 +++++++++++++++++++
 .../fit/CXFOAuth2HttpUriRequestFactory.java     | 131 --------------
 .../apache/olingo/fit/v4/OAuth2TestITCase.java  |  27 +--
 .../api/http/WrappingHttpClientFactory.java     |  24 +++
 .../http/AbstractOAuth2HttpClientFactory.java   | 116 ++++++++++++
 .../AbstractOAuth2HttpUriRequestFactory.java    |  53 ------
 .../client/core/http/OAuth2Exception.java       |   4 +
 .../http/ProxyWrapperHttpClientFactory.java     |  96 ----------
 .../http/ProxyWrappingHttpClientFactory.java    |  97 ++++++++++
 .../AbstractODataDeserializer.java              |  15 +-
 .../apache/olingo/client/core/uri/URIUtils.java |   6 +-
 .../core/serialization/JsonDeserializer.java    | 148 ++++++++-------
 17 files changed, 572 insertions(+), 473 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/main/java/org/apache/olingo/fit/V4OAuth2.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/V4OAuth2.java b/fit/src/main/java/org/apache/olingo/fit/V4OAuth2.java
index 02dc887..dd5e89e 100644
--- a/fit/src/main/java/org/apache/olingo/fit/V4OAuth2.java
+++ b/fit/src/main/java/org/apache/olingo/fit/V4OAuth2.java
@@ -18,16 +18,12 @@
  */
 package org.apache.olingo.fit;
 
-import org.apache.cxf.interceptor.InInterceptors;
-import org.apache.olingo.fit.rest.OAuth2InInterceptor;
 import org.springframework.stereotype.Service;
-
 import javax.ws.rs.Path;
 import java.io.IOException;
 
 @Service
 @Path("/V40/OAuth2.svc")
-@InInterceptors(classes = {OAuth2InInterceptor.class})
 public class V4OAuth2 extends V4Services {
 
   public V4OAuth2() throws IOException {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2InInterceptor.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2InInterceptor.java b/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2InInterceptor.java
deleted file mode 100644
index d61d3f0..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2InInterceptor.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.fit.rest;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.cxf.message.Message;
-import org.apache.cxf.phase.AbstractPhaseInterceptor;
-import org.apache.cxf.phase.Phase;
-import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
-import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
-import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant;
-import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
-import org.apache.cxf.transport.http.AbstractHTTPDestination;
-
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-
-public class OAuth2InInterceptor extends AbstractPhaseInterceptor<Message> {
-
-  private static final OAuthClientUtils.Consumer OAUTH2_CONSUMER =
-          new OAuthClientUtils.Consumer(OAuth2Provider.CLIENT_ID, OAuth2Provider.CLIENT_SECRET);
-
-  public OAuth2InInterceptor() {
-    super(Phase.PRE_INVOKE);
-  }
-
-  @Override
-  public void handleMessage(final Message message) throws Fault {
-    final String requestURL = (String) message.get(Message.REQUEST_URL);
-    if (requestURL.contains("V40/OAuth2.svc")) {
-      @SuppressWarnings("unchecked")
-      final Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
-      final List<String> oauth2CodeHeader = headers.get(OAuth2Provider.OAUTH2_CODE_HEADER);
-      if (oauth2CodeHeader == null || oauth2CodeHeader.isEmpty()) {
-        message.put(AbstractHTTPDestination.REQUEST_REDIRECTED, Boolean.TRUE);
-
-        final HttpServletResponse response = (HttpServletResponse) message.get(AbstractHTTPDestination.HTTP_RESPONSE);
-        try {
-          final String authorizationServiceURI =
-                  StringUtils.substringBefore(requestURL, "V40/OAuth2.svc") + "oauth/authorize";
-
-          final URI authorizationURI = OAuthClientUtils.getAuthorizationURI(
-                  authorizationServiceURI,
-                  OAuth2Provider.CLIENT_ID,
-                  OAuth2Provider.REDIRECT_URI,
-                  null,
-                  null);
-          response.addHeader("Location", authorizationURI.toASCIIString());
-          response.sendError(303);
-        } catch (Exception e) {
-          throw new Fault(e);
-        }
-      } else {
-        try {
-          final JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
-          bean.setAddress(StringUtils.substringBefore(requestURL, "V40/OAuth2.svc") + "oauth/token");
-          bean.setUsername("odatajclient");
-          bean.setPassword("odatajclient");
-          final WebClient accessTokenService = bean.createWebClient().
-                  type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).
-                  accept(MediaType.APPLICATION_JSON_TYPE);
-
-          final AuthorizationCodeGrant codeGrant = new AuthorizationCodeGrant(oauth2CodeHeader.get(0));
-          final ClientAccessToken accessToken =
-                  OAuthClientUtils.getAccessToken(accessTokenService, OAUTH2_CONSUMER, codeGrant);
-          if (accessToken == null) {
-            throw new WebApplicationException("No OAuth2 access token");
-          }
-        } catch (OAuthServiceException e) {
-          throw new Fault(e);
-        }
-      }
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2Provider.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2Provider.java b/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2Provider.java
index cfe25b9..7000650 100644
--- a/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2Provider.java
+++ b/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2Provider.java
@@ -40,8 +40,6 @@ public class OAuth2Provider implements AuthorizationCodeDataProvider {
 
   public static final String REDIRECT_URI = "/stub/StaticService/V40/OAuth2.svc/";
 
-  public static final String OAUTH2_CODE_HEADER = "oauth2.token";
-
   private Client client;
 
   private ServerAuthorizationCodeGrant grant;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2RequestFilter.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2RequestFilter.java b/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2RequestFilter.java
new file mode 100644
index 0000000..9022374
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/rest/OAuth2RequestFilter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.fit.rest;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.ext.Provider;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter;
+
+@Provider
+public class OAuth2RequestFilter extends OAuthRequestFilter implements ContainerRequestFilter {
+
+  @Override
+  public void filter(final ContainerRequestContext context) {
+    final String svcName =
+            StringUtils.substringBefore(StringUtils.substringAfter(context.getUriInfo().getPath(), "/"), "/");
+    if ("OAuth2.svc".equals(svcName)) {
+      super.filter(context);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/main/webapp/WEB-INF/applicationContext.xml
----------------------------------------------------------------------
diff --git a/fit/src/main/webapp/WEB-INF/applicationContext.xml b/fit/src/main/webapp/WEB-INF/applicationContext.xml
index 2cadd61..f1320b9 100644
--- a/fit/src/main/webapp/WEB-INF/applicationContext.xml
+++ b/fit/src/main/webapp/WEB-INF/applicationContext.xml
@@ -40,6 +40,9 @@
   <jaxrs:server id="services" address="/" basePackages="org.apache.olingo.fit">
     <jaxrs:providers>
       <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
+      <bean class="org.apache.olingo.fit.rest.OAuth2RequestFilter">
+        <property name="dataProvider" ref="oauthProvider"/>
+      </bean>
       <bean class="org.apache.olingo.fit.rest.ServiceNameResponseFilter"/>
     </jaxrs:providers>
   </jaxrs:server>
@@ -52,7 +55,7 @@
     <property name="dataProvider" ref="oauthProvider"/>
   </bean>  
   <bean id="oauthSecurityInterceptor" class="org.apache.olingo.fit.rest.StaticSecurityInterceptor"/>
-  <jaxrs:server id="oauthServer" address="/oauth">
+  <jaxrs:server id="oauthServer" address="/oauth2">
     <jaxrs:serviceBeans>
       <ref bean="authorizationService"/>
       <ref bean="accessTokenService"/>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpClientFactory.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpClientFactory.java b/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpClientFactory.java
new file mode 100644
index 0000000..28ff3d8
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpClientFactory.java
@@ -0,0 +1,181 @@
+/*
+ * 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.fit;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import java.io.IOException;
+import java.net.URI;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
+import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant;
+import org.apache.cxf.rs.security.oauth2.grants.refresh.RefreshTokenGrant;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.apache.olingo.client.core.http.AbstractOAuth2HttpClientFactory;
+import org.apache.olingo.client.core.http.OAuth2Exception;
+import org.apache.olingo.fit.rest.OAuth2Provider;
+
+public class CXFOAuth2HttpClientFactory extends AbstractOAuth2HttpClientFactory {
+
+  private static final OAuthClientUtils.Consumer OAUTH2_CONSUMER =
+          new OAuthClientUtils.Consumer(OAuth2Provider.CLIENT_ID, OAuth2Provider.CLIENT_SECRET);
+
+  private ClientAccessToken accessToken;
+
+  public CXFOAuth2HttpClientFactory(final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
+    super(oauth2GrantServiceURI, oauth2TokenServiceURI);
+  }
+
+  private WebClient getAccessTokenService() {
+    final JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+    bean.setAddress(oauth2TokenServiceURI.toASCIIString());
+    bean.setUsername("odatajclient");
+    bean.setPassword("odatajclient");
+    return bean.createWebClient().
+            type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).accept(MediaType.APPLICATION_JSON_TYPE);
+  }
+
+  @Override
+  protected boolean isInited() throws OAuth2Exception {
+    return accessToken != null;
+  }
+
+  @Override
+  protected void init() throws OAuth2Exception {
+    final URI authURI = OAuthClientUtils.getAuthorizationURI(
+            oauth2GrantServiceURI.toASCIIString(),
+            OAuth2Provider.CLIENT_ID,
+            OAuth2Provider.REDIRECT_URI,
+            null,
+            null);
+
+    // Disable automatic redirects handling
+    final HttpParams params = new BasicHttpParams();
+    params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
+    final DefaultHttpClient httpClient = new DefaultHttpClient(params);
+
+    JsonNode oAuthAuthorizationData = null;
+    String authenticityCookie = null;
+    try {
+      // 1. Need to (basic) authenticate against the OAuth2 service
+      final HttpGet method = new HttpGet(authURI);
+      method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes()));
+      final HttpResponse response = httpClient.execute(method);
+
+      // 2. Pull out OAuth2 authorization data and "authenticity" cookie (CXF specific)
+      oAuthAuthorizationData = new XmlMapper().readTree(EntityUtils.toString(response.getEntity()));
+
+      final Header setCookieHeader = response.getFirstHeader("Set-Cookie");
+      if (setCookieHeader == null) {
+        throw new IllegalStateException("OAuth flow is broken");
+      }
+      authenticityCookie = setCookieHeader.getValue();
+    } catch (Exception e) {
+      throw new OAuth2Exception(e);
+    }
+
+    String code = null;
+    try {
+      // 3. Submit the HTTP form for allowing access to the application
+      final URI location = new URIBuilder(oAuthAuthorizationData.get("replyTo").asText()).
+              addParameter("session_authenticity_token", oAuthAuthorizationData.get("authenticityToken").asText()).
+              addParameter("client_id", oAuthAuthorizationData.get("clientId").asText()).
+              addParameter("redirect_uri", oAuthAuthorizationData.get("redirectUri").asText()).
+              addParameter("oauthDecision", "allow").
+              build();
+      final HttpGet method = new HttpGet(location);
+      method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes()));
+      method.addHeader("Cookie", authenticityCookie);
+
+      final HttpResponse response = httpClient.execute(method);
+
+      final Header locationHeader = response.getFirstHeader("Location");
+      if (response.getStatusLine().getStatusCode() != 303 || locationHeader == null) {
+        throw new IllegalStateException("OAuth flow is broken");
+      }
+
+      // 4. Get the authorization code value out of this last redirect
+      code = StringUtils.substringAfterLast(locationHeader.getValue(), "=");
+
+      EntityUtils.consumeQuietly(response.getEntity());
+    } catch (Exception e) {
+      throw new OAuth2Exception(e);
+    }
+
+    // 5. Obtain the access token
+    try {
+      accessToken = OAuthClientUtils.getAccessToken(
+              getAccessTokenService(), OAUTH2_CONSUMER, new AuthorizationCodeGrant(code));
+    } catch (OAuthServiceException e) {
+      throw new OAuth2Exception(e);
+    }
+
+    if (accessToken == null) {
+      throw new OAuth2Exception("No OAuth2 access token");
+    }
+  }
+
+  @Override
+  protected void accessToken(final DefaultHttpClient client) throws OAuth2Exception {
+    client.addRequestInterceptor(new HttpRequestInterceptor() {
+
+      @Override
+      public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+        request.removeHeaders(HttpHeaders.AUTHORIZATION);
+        request.addHeader(HttpHeaders.AUTHORIZATION, OAuthClientUtils.createAuthorizationHeader(accessToken));
+      }
+    });
+  }
+
+  @Override
+  protected void refreshToken(final DefaultHttpClient client) throws OAuth2Exception {
+    final String refreshToken = accessToken.getRefreshToken();
+    if (refreshToken == null) {
+      throw new OAuth2Exception("No OAuth2 refresh token");
+    }
+
+    // refresh the token
+    try {
+      accessToken = OAuthClientUtils.getAccessToken(
+              getAccessTokenService(), OAUTH2_CONSUMER, new RefreshTokenGrant(refreshToken));
+    } catch (OAuthServiceException e) {
+      throw new OAuth2Exception(e);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpUriRequestFactory.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpUriRequestFactory.java b/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpUriRequestFactory.java
deleted file mode 100644
index a682a52..0000000
--- a/fit/src/test/java/org/apache/olingo/fit/CXFOAuth2HttpUriRequestFactory.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.fit;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.dataformat.xml.XmlMapper;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.util.EntityUtils;
-import org.apache.olingo.client.core.http.AbstractOAuth2HttpUriRequestFactory;
-import org.apache.olingo.client.core.http.OAuth2Exception;
-import org.apache.olingo.fit.rest.OAuth2Provider;
-
-import java.net.URI;
-
-public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequestFactory {
-
-  private String code;
-
-  public CXFOAuth2HttpUriRequestFactory(final URI redirectURI) {
-    super(redirectURI);
-  }
-
-  @Override
-  protected boolean isInited() {
-    return code != null;
-  }
-
-  @Override
-  protected void init() throws OAuth2Exception {
-    // 1. Disable automatic redirects handling
-    final HttpParams params = new BasicHttpParams();
-    params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
-    final DefaultHttpClient httpClient = new DefaultHttpClient(params);
-
-    // 2. Try to access the redirect URI without any special header: get redirected to the OAuth2 service
-    URI location = null;
-    try {
-      final HttpResponse response = httpClient.execute(new HttpGet(redirectURI));
-
-      final Header locationHeader = response.getFirstHeader("Location");
-      if (response.getStatusLine().getStatusCode() != 303 || locationHeader == null) {
-        throw new IllegalStateException("OAuth flow is broken");
-      }
-
-      location = new URI(locationHeader.getValue());
-
-      EntityUtils.consumeQuietly(response.getEntity());
-    } catch (Exception e) {
-      throw new OAuth2Exception(e);
-    }
-
-    JsonNode oAuthAuthorizationData = null;
-    String authenticityCookie = null;
-    try {
-      // 3. Need to (basic) authenticate against the OAuth2 service
-      final HttpGet method = new HttpGet(location);
-      method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes()));
-      final HttpResponse response = httpClient.execute(method);
-
-      // 4. Pull out OAuth2 authorization data and "authenticity" cookie (CXF specific)
-      oAuthAuthorizationData = new XmlMapper().readTree(EntityUtils.toString(response.getEntity()));
-
-      final Header setCookieHeader = response.getFirstHeader("Set-Cookie");
-      if (setCookieHeader == null) {
-        throw new IllegalStateException("OAuth flow is broken");
-      }
-      authenticityCookie = setCookieHeader.getValue();
-    } catch (Exception e) {
-      throw new OAuth2Exception(e);
-    }
-
-    try {
-      // 5. Submit the HTTP form for allowing access to the application
-      location = new URIBuilder(oAuthAuthorizationData.get("replyTo").asText()).
-              addParameter("session_authenticity_token", oAuthAuthorizationData.get("authenticityToken").asText()).
-              addParameter("client_id", oAuthAuthorizationData.get("clientId").asText()).
-              addParameter("redirect_uri", oAuthAuthorizationData.get("redirectUri").asText()).
-              addParameter("oauthDecision", "allow").
-              build();
-      final HttpGet method = new HttpGet(location);
-      method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes()));
-      method.addHeader("Cookie", authenticityCookie);
-
-      final HttpResponse response = httpClient.execute(method);
-
-      final Header locationHeader = response.getFirstHeader("Location");
-      if (response.getStatusLine().getStatusCode() != 303 || locationHeader == null) {
-        throw new IllegalStateException("OAuth flow is broken");
-      }
-
-      // 6. Finally get the code value out of this last redirect
-      code = StringUtils.substringAfterLast(locationHeader.getValue(), "=");
-
-      EntityUtils.consumeQuietly(response.getEntity());
-    } catch (Exception e) {
-      throw new OAuth2Exception(e);
-    }
-  }
-
-  @Override
-  protected void sign(final HttpUriRequest request) {
-    request.addHeader(OAuth2Provider.OAUTH2_CODE_HEADER, code);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/fit/src/test/java/org/apache/olingo/fit/v4/OAuth2TestITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/v4/OAuth2TestITCase.java b/fit/src/test/java/org/apache/olingo/fit/v4/OAuth2TestITCase.java
index 8595c3f..be8eabb 100644
--- a/fit/src/test/java/org/apache/olingo/fit/v4/OAuth2TestITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/v4/OAuth2TestITCase.java
@@ -18,6 +18,9 @@
  */
 package org.apache.olingo.fit.v4;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
@@ -25,39 +28,41 @@ import org.apache.olingo.client.api.uri.v4.URIBuilder;
 import org.apache.olingo.client.api.v4.EdmEnabledODataClient;
 import org.apache.olingo.client.api.v4.ODataClient;
 import org.apache.olingo.client.core.ODataClientFactory;
-import org.apache.olingo.client.core.http.DefaultHttpUriRequestFactory;
 import org.apache.olingo.commons.api.domain.v4.ODataEntity;
 import org.apache.olingo.commons.api.format.ODataFormat;
-import org.apache.olingo.fit.CXFOAuth2HttpUriRequestFactory;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
-
 import java.net.URI;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import org.apache.olingo.client.core.http.DefaultHttpClientFactory;
+import org.apache.olingo.fit.CXFOAuth2HttpClientFactory;
 
 public class OAuth2TestITCase extends AbstractTestITCase {
 
+  private static final URI OAUTH2_GRANT_SERVICE_URI =
+          URI.create("http://localhost:9080/stub/StaticService/oauth2/authorize");
+
+  private static final URI OAUTH2_TOKEN_SERVICE_URI =
+          URI.create("http://localhost:9080/stub/StaticService/oauth2/token");
+
   private EdmEnabledODataClient _edmClient;
 
   @BeforeClass
   public static void enableOAuth2() {
-    client.getConfiguration().setHttpUriRequestFactory(
-            new CXFOAuth2HttpUriRequestFactory(URI.create(testOAuth2ServiceRootURL)));
+    client.getConfiguration().setHttpClientFactory(
+            new CXFOAuth2HttpClientFactory(OAUTH2_GRANT_SERVICE_URI, OAUTH2_TOKEN_SERVICE_URI));
   }
 
   @AfterClass
   public static void disableOAuth2() {
-    client.getConfiguration().setHttpUriRequestFactory(new DefaultHttpUriRequestFactory());
+    client.getConfiguration().setHttpClientFactory(new DefaultHttpClientFactory());
   }
 
   protected EdmEnabledODataClient getEdmClient() {
     if (_edmClient == null) {
       _edmClient = ODataClientFactory.getEdmEnabledV4(testOAuth2ServiceRootURL);
-      _edmClient.getConfiguration().setHttpUriRequestFactory(
-              new CXFOAuth2HttpUriRequestFactory(URI.create(testOAuth2ServiceRootURL)));
+      _edmClient.getConfiguration().setHttpClientFactory(
+              new CXFOAuth2HttpClientFactory(OAUTH2_GRANT_SERVICE_URI, OAUTH2_TOKEN_SERVICE_URI));
     }
 
     return _edmClient;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-api/src/main/java/org/apache/olingo/client/api/http/WrappingHttpClientFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/http/WrappingHttpClientFactory.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/http/WrappingHttpClientFactory.java
new file mode 100644
index 0000000..244cc5f
--- /dev/null
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/http/WrappingHttpClientFactory.java
@@ -0,0 +1,24 @@
+/*
+ * 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.http;
+
+public interface WrappingHttpClientFactory extends HttpClientFactory {
+
+  HttpClientFactory getWrappedHttpClientFactory();
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpClientFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpClientFactory.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpClientFactory.java
new file mode 100644
index 0000000..dd8b485
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpClientFactory.java
@@ -0,0 +1,116 @@
+/*
+ * 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.http;
+
+import java.io.IOException;
+import java.net.URI;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+import org.apache.olingo.client.api.http.HttpClientFactory;
+import org.apache.olingo.client.api.http.HttpMethod;
+import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
+
+public abstract class AbstractOAuth2HttpClientFactory
+        extends AbstractHttpClientFactory implements WrappingHttpClientFactory {
+
+  protected final DefaultHttpClientFactory wrapped;
+
+  protected final URI oauth2GrantServiceURI;
+
+  protected final URI oauth2TokenServiceURI;
+
+  protected HttpUriRequest currentRequest;
+
+  public AbstractOAuth2HttpClientFactory(final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
+    this(new DefaultHttpClientFactory(), oauth2GrantServiceURI, oauth2TokenServiceURI);
+  }
+
+  public AbstractOAuth2HttpClientFactory(final DefaultHttpClientFactory wrapped,
+          final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
+
+    super();
+    this.wrapped = wrapped;
+    this.oauth2GrantServiceURI = oauth2GrantServiceURI;
+    this.oauth2TokenServiceURI = oauth2TokenServiceURI;
+  }
+
+  @Override
+  public HttpClientFactory getWrappedHttpClientFactory() {
+    return wrapped;
+  }
+
+  protected abstract boolean isInited() throws OAuth2Exception;
+
+  protected abstract void init() throws OAuth2Exception;
+
+  protected abstract void accessToken(DefaultHttpClient client) throws OAuth2Exception;
+
+  protected abstract void refreshToken(DefaultHttpClient client) throws OAuth2Exception;
+
+  @Override
+  public HttpClient create(final HttpMethod method, final URI uri) {
+    if (!isInited()) {
+      init();
+    }
+
+    final DefaultHttpClient httpClient = wrapped.create(method, uri);
+    accessToken(httpClient);
+
+    httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
+
+      @Override
+      public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+        if (request instanceof HttpUriRequest) {
+          currentRequest = (HttpUriRequest) request;
+        } else {
+          currentRequest = null;
+        }
+      }
+    });
+    httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
+
+      @Override
+      public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+          refreshToken(httpClient);
+
+          if (currentRequest != null) {
+            httpClient.execute(currentRequest);
+          }
+        }
+      }
+    });
+
+    return httpClient;
+  }
+
+  @Override
+  public void close(final HttpClient httpClient) {
+    wrapped.close(httpClient);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpUriRequestFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpUriRequestFactory.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpUriRequestFactory.java
deleted file mode 100644
index e756dcb..0000000
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/AbstractOAuth2HttpUriRequestFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.http;
-
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.olingo.client.api.http.HttpMethod;
-
-import java.net.URI;
-
-public abstract class AbstractOAuth2HttpUriRequestFactory extends DefaultHttpUriRequestFactory {
-
-  protected final URI redirectURI;
-
-  public AbstractOAuth2HttpUriRequestFactory(final URI redirectURI) {
-    this.redirectURI = redirectURI;
-  }
-
-  protected abstract boolean isInited();
-
-  protected abstract void init() throws OAuth2Exception;
-
-  protected abstract void sign(HttpUriRequest request);
-
-  @Override
-  public HttpUriRequest create(final HttpMethod method, final URI uri) {
-    if (!isInited()) {
-      init();
-    }
-
-    final HttpUriRequest request = super.create(method, uri);
-
-    sign(request);
-
-    return request;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/http/OAuth2Exception.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/OAuth2Exception.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/OAuth2Exception.java
index 8158a51..9462ebc 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/OAuth2Exception.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/OAuth2Exception.java
@@ -22,6 +22,10 @@ public class OAuth2Exception extends RuntimeException {
 
   private static final long serialVersionUID = 5695438980473040134L;
 
+  public OAuth2Exception(final String message) {
+    super(message);
+  }
+
   public OAuth2Exception(final Throwable cause) {
     super(cause);
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrapperHttpClientFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrapperHttpClientFactory.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrapperHttpClientFactory.java
deleted file mode 100644
index 365df37..0000000
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrapperHttpClientFactory.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.http;
-
-import org.apache.http.HttpHost;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.conn.params.ConnRoutePNames;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.olingo.client.api.http.HttpClientFactory;
-import org.apache.olingo.client.api.http.HttpMethod;
-
-import java.net.URI;
-
-/**
- * Implementation for working behind an HTTP proxy (possibly requiring authentication); requires another concrete
- * {@link HttpClientFactory} implementation acting as real HTTP client factory.
- */
-public class ProxyWrapperHttpClientFactory implements HttpClientFactory {
-
-  private final URI proxy;
-
-  private String proxyUsername;
-
-  private String proxyPassword;
-
-  private final DefaultHttpClientFactory wrapped;
-
-  public ProxyWrapperHttpClientFactory(final URI proxy) {
-    this(proxy, null, null, new DefaultHttpClientFactory());
-  }
-
-  public ProxyWrapperHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) {
-    this(proxy, proxyUsername, proxyPassword, new DefaultHttpClientFactory());
-  }
-
-  public ProxyWrapperHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) {
-    this(proxy, null, null, wrapped);
-  }
-
-  public ProxyWrapperHttpClientFactory(final URI proxy,
-          final String proxyUsername, final String proxyPassword, final DefaultHttpClientFactory wrapped) {
-
-    this.proxy = proxy;
-    this.proxyUsername = proxyUsername;
-    this.proxyPassword = proxyPassword;
-    this.wrapped = wrapped;
-  }
-
-  public DefaultHttpClientFactory getWrappedHttpClientFactory() {
-    return this.wrapped;
-  }
-
-  @Override
-  public HttpClient create(final HttpMethod method, final URI uri) {
-    // Use wrapped factory to obtain an httpclient instance for given method and uri
-    final DefaultHttpClient httpclient = wrapped.create(method, uri);
-
-    final HttpHost proxyHost = new HttpHost(proxy.getHost(), proxy.getPort());
-
-    // Sets usage of HTTP proxy
-    httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
-
-    // Sets proxy authentication, if credentials were provided
-    if (proxyUsername != null && proxyPassword != null) {
-      httpclient.getCredentialsProvider().setCredentials(
-              new AuthScope(proxyHost),
-              new UsernamePasswordCredentials(proxyUsername, proxyPassword));
-    }
-
-    return httpclient;
-  }
-
-  @Override
-  public void close(final HttpClient httpClient) {
-    wrapped.close(httpClient);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrappingHttpClientFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrappingHttpClientFactory.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrappingHttpClientFactory.java
new file mode 100644
index 0000000..e371c5e
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/http/ProxyWrappingHttpClientFactory.java
@@ -0,0 +1,97 @@
+/*
+ * 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.http;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.olingo.client.api.http.HttpClientFactory;
+import org.apache.olingo.client.api.http.HttpMethod;
+
+import java.net.URI;
+import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
+
+/**
+ * Implementation for working behind an HTTP proxy (possibly requiring authentication); requires another concrete
+ * {@link HttpClientFactory} implementation acting as real HTTP client factory.
+ */
+public class ProxyWrappingHttpClientFactory implements WrappingHttpClientFactory {
+
+  private final URI proxy;
+
+  private String proxyUsername;
+
+  private String proxyPassword;
+
+  private final DefaultHttpClientFactory wrapped;
+
+  public ProxyWrappingHttpClientFactory(final URI proxy) {
+    this(proxy, null, null, new DefaultHttpClientFactory());
+  }
+
+  public ProxyWrappingHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) {
+    this(proxy, proxyUsername, proxyPassword, new DefaultHttpClientFactory());
+  }
+
+  public ProxyWrappingHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) {
+    this(proxy, null, null, wrapped);
+  }
+
+  public ProxyWrappingHttpClientFactory(final URI proxy,
+          final String proxyUsername, final String proxyPassword, final DefaultHttpClientFactory wrapped) {
+
+    this.proxy = proxy;
+    this.proxyUsername = proxyUsername;
+    this.proxyPassword = proxyPassword;
+    this.wrapped = wrapped;
+  }
+
+  public DefaultHttpClientFactory getWrappedHttpClientFactory() {
+    return this.wrapped;
+  }
+
+  @Override
+  public HttpClient create(final HttpMethod method, final URI uri) {
+    // Use wrapped factory to obtain an httpclient instance for given method and uri
+    final DefaultHttpClient httpclient = wrapped.create(method, uri);
+
+    final HttpHost proxyHost = new HttpHost(proxy.getHost(), proxy.getPort());
+
+    // Sets usage of HTTP proxy
+    httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
+
+    // Sets proxy authentication, if credentials were provided
+    if (proxyUsername != null && proxyPassword != null) {
+      httpclient.getCredentialsProvider().setCredentials(
+              new AuthScope(proxyHost),
+              new UsernamePasswordCredentials(proxyUsername, proxyPassword));
+    }
+
+    return httpclient;
+  }
+
+  @Override
+  public void close(final HttpClient httpClient) {
+    wrapped.close(httpClient);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AbstractODataDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AbstractODataDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AbstractODataDeserializer.java
index 0e11c58..e739ec8 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AbstractODataDeserializer.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AbstractODataDeserializer.java
@@ -46,10 +46,11 @@ import java.io.InputStream;
 public abstract class AbstractODataDeserializer {
 
   protected final ODataServiceVersion version;
+
   protected final ODataDeserializer deserializer;
 
   public AbstractODataDeserializer(final ODataServiceVersion version, final boolean serverMode,
-      final ODataFormat format) {
+          final ODataFormat format) {
     this.version = version;
     if (format == ODataFormat.XML || format == ODataFormat.ATOM) {
       deserializer = new AtomDeserializer(version);
@@ -76,18 +77,18 @@ public abstract class AbstractODataDeserializer {
 
   protected XmlMapper getXmlMapper() {
     final XmlMapper xmlMapper = new XmlMapper(
-        new XmlFactory(new InputFactoryImpl(), new OutputFactoryImpl()), new JacksonXmlModule());
+            new XmlFactory(new InputFactoryImpl(), new OutputFactoryImpl()), new JacksonXmlModule());
 
     xmlMapper.setInjectableValues(new InjectableValues.Std().
-        addValue(ODataServiceVersion.class, version).
-        addValue(Boolean.class, Boolean.FALSE));
+            addValue(ODataServiceVersion.class, version).
+            addValue(Boolean.class, Boolean.FALSE));
 
     xmlMapper.addHandler(new DeserializationProblemHandler() {
       @Override
       public boolean handleUnknownProperty(final DeserializationContext ctxt, final JsonParser jp,
-          final com.fasterxml.jackson.databind.JsonDeserializer<?> deserializer,
-          final Object beanOrClass, final String propertyName)
-          throws IOException, JsonProcessingException {
+              final com.fasterxml.jackson.databind.JsonDeserializer<?> deserializer,
+              final Object beanOrClass, final String propertyName)
+              throws IOException, JsonProcessingException {
 
         // skip any unknown property
         ctxt.getParser().skipChildren();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIUtils.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIUtils.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIUtils.java
index 8ddc273..b81b977 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIUtils.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIUtils.java
@@ -31,7 +31,6 @@ import org.apache.olingo.client.api.CommonODataClient;
 import org.apache.olingo.client.api.http.HttpClientFactory;
 import org.apache.olingo.client.api.uri.SegmentType;
 import org.apache.olingo.client.core.http.BasicAuthHttpClientFactory;
-import org.apache.olingo.client.core.http.ProxyWrapperHttpClientFactory;
 import org.apache.olingo.commons.api.Constants;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@@ -65,6 +64,7 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 import java.util.regex.Pattern;
+import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
 
 /**
  * URI utilities.
@@ -354,8 +354,8 @@ public final class URIUtils {
     HttpClientFactory httpclientFactory = client.getConfiguration().getHttpClientFactory();
     if (httpclientFactory instanceof BasicAuthHttpClientFactory) {
       return true;
-    } else if (httpclientFactory instanceof ProxyWrapperHttpClientFactory) {
-      ProxyWrapperHttpClientFactory tmp = (ProxyWrapperHttpClientFactory) httpclientFactory;
+    } else if (httpclientFactory instanceof WrappingHttpClientFactory) {
+      WrappingHttpClientFactory tmp = (WrappingHttpClientFactory) httpclientFactory;
       if (tmp.getWrappedHttpClientFactory() instanceof BasicAuthHttpClientFactory) {
         return true;
       }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c155238d/lib/commons-core/src/main/java/org/apache/olingo/commons/core/serialization/JsonDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/serialization/JsonDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/serialization/JsonDeserializer.java
index 1563e17..e1b8b1f 100755
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/serialization/JsonDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/serialization/JsonDeserializer.java
@@ -53,7 +53,6 @@ import org.apache.olingo.commons.core.data.LinkImpl;
 import org.apache.olingo.commons.core.data.LinkedComplexValueImpl;
 import org.apache.olingo.commons.core.data.PropertyImpl;
 import org.apache.olingo.commons.core.edm.EdmTypeInfo;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.AbstractMap.SimpleEntry;
@@ -69,23 +68,39 @@ import java.util.regex.Pattern;
 public class JsonDeserializer implements ODataDeserializer {
 
   protected final Pattern CUSTOM_ANNOTATION = Pattern.compile("(.+)@(.+)\\.(.+)");
+
   protected final ODataServiceVersion version;
+
   protected final boolean serverMode;
 
   protected String jsonType;
+
   protected String jsonId;
+
   protected String jsonETag;
+
   protected String jsonReadLink;
+
   protected String jsonEditLink;
+
   protected String jsonMediaEditLink;
+
   protected String jsonMediaReadLink;
+
   protected String jsonMediaContentType;
+
   protected String jsonMediaETag;
+
   protected String jsonAssociationLink;
+
   protected String jsonNavigationLink;
+
   protected String jsonCount;
+
   protected String jsonNextLink;
+
   protected String jsonDeltaLink;
+
   protected String jsonError;
 
   private JsonGeoValueDeserializer geoDeserializer;
@@ -129,7 +144,7 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   protected String setInline(final String name, final String suffix, final JsonNode tree,
-      final ObjectCodec codec, final LinkImpl link) throws IOException {
+          final ObjectCodec codec, final LinkImpl link) throws IOException {
 
     final String entityNamePrefix = name.substring(0, name.indexOf(suffix));
     if (tree.has(entityNamePrefix)) {
@@ -143,11 +158,9 @@ public class JsonDeserializer implements ODataDeserializer {
       } else if (inline instanceof ArrayNode) {
         link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
 
-        EntitySet entitySet = new EntitySetImpl();
-        Iterator<JsonNode> entries = inline.elements();
-        while (entries.hasNext()) {
-          entitySet.getEntities().add(
-              entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload());
+        final EntitySet entitySet = new EntitySetImpl();
+        for (final Iterator<JsonNode> entries = inline.elements(); entries.hasNext();) {
+          entitySet.getEntities().add(entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload());
         }
 
         link.setInlineEntitySet(entitySet);
@@ -157,7 +170,7 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   protected void links(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove,
-      final JsonNode tree, final ObjectCodec codec) throws IOException {
+          final JsonNode tree, final ObjectCodec codec) throws IOException {
     if (serverMode) {
       serverLinks(field, linked, toRemove, tree, codec);
     } else {
@@ -166,7 +179,7 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   private void clientLinks(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove,
-      final JsonNode tree, final ObjectCodec codec) throws IOException {
+          final JsonNode tree, final ObjectCodec codec) throws IOException {
 
     if (field.getKey().endsWith(jsonNavigationLink)) {
       final LinkImpl link = new LinkImpl();
@@ -195,10 +208,10 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   private void serverLinks(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove,
-      final JsonNode tree, final ObjectCodec codec) throws IOException {
+          final JsonNode tree, final ObjectCodec codec) throws IOException {
 
     if (field.getKey().endsWith(Constants.JSON_BIND_LINK_SUFFIX)
-        || field.getKey().endsWith(jsonNavigationLink)) {
+            || field.getKey().endsWith(jsonNavigationLink)) {
 
       if (field.getValue().isValueNode()) {
         final String suffix = field.getKey().replaceAll("^.*@", "@");
@@ -258,7 +271,7 @@ public class JsonDeserializer implements ODataDeserializer {
       if (node.has(Constants.ATTR_TYPE)) {
         type = ODataPropertyType.PRIMITIVE;
         typeInfo = new EdmTypeInfo.Builder().
-            setTypeExpression("Edm.Geography" + node.get(Constants.ATTR_TYPE).asText()).build();
+                setTypeExpression("Edm.Geography" + node.get(Constants.ATTR_TYPE).asText()).build();
       } else {
         type = ODataPropertyType.COMPLEX;
       }
@@ -270,8 +283,8 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   protected void populate(final Annotatable annotatable, final List<Property> properties,
-      final ObjectNode tree, final ObjectCodec codec)
-      throws IOException, EdmPrimitiveTypeException {
+          final ObjectNode tree, final ObjectCodec codec)
+          throws IOException, EdmPrimitiveTypeException {
 
     String type = null;
     Annotation annotation = null;
@@ -297,8 +310,8 @@ public class JsonDeserializer implements ODataDeserializer {
         final PropertyImpl property = new PropertyImpl();
         property.setName(field.getKey());
         property.setType(type == null
-            ? null
-            : new EdmTypeInfo.Builder().setTypeExpression(type).build().internal());
+                ? null
+                : new EdmTypeInfo.Builder().setTypeExpression(type).build().internal());
         type = null;
 
         value(property, field.getValue(), codec);
@@ -313,25 +326,25 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   private Object fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) throws EdmPrimitiveTypeException {
-    return node.isNull() ? null :
-        typeInfo == null ? node.asText() :
-            typeInfo.getPrimitiveTypeKind().isGeospatial() ?
-                getGeoDeserializer().deserialize(node, typeInfo) :
-                ((EdmPrimitiveType) typeInfo.getType())
-                    .valueOfString(node.asText(), true, null,
-                        Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true,
-                        ((EdmPrimitiveType) typeInfo.getType()).getDefaultType());
+    return node.isNull() ? null
+            : typeInfo == null ? node.asText()
+            : typeInfo.getPrimitiveTypeKind().isGeospatial()
+            ? getGeoDeserializer().deserialize(node, typeInfo)
+            : ((EdmPrimitiveType) typeInfo.getType())
+            .valueOfString(node.asText(), true, null,
+                    Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true,
+                    ((EdmPrimitiveType) typeInfo.getType()).getDefaultType());
   }
 
   private Object fromComplex(final ObjectNode node, final ObjectCodec codec)
-      throws IOException, EdmPrimitiveTypeException {
+          throws IOException, EdmPrimitiveTypeException {
 
     if (version.compareTo(ODataServiceVersion.V40) < 0) {
-      List<Property> properties = new ArrayList<Property>();
+      final List<Property> properties = new ArrayList<Property>();
       populate(null, properties, node, codec);
       return properties;
     } else {
-      LinkedComplexValue linkComplexValue = new LinkedComplexValueImpl();
+      final LinkedComplexValue linkComplexValue = new LinkedComplexValueImpl();
       final Set<String> toRemove = new HashSet<String>();
       for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
         final Map.Entry<String, JsonNode> field = itor.next();
@@ -346,13 +359,13 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   private void fromCollection(final Valuable valuable, final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo,
-      final ObjectCodec codec) throws IOException, EdmPrimitiveTypeException {
+          final ObjectCodec codec) throws IOException, EdmPrimitiveTypeException {
 
-    List<Object> values = new ArrayList<Object>();
+    final List<Object> values = new ArrayList<Object>();
     ValueType valueType = ValueType.COLLECTION_PRIMITIVE;
 
-    final EdmTypeInfo type = typeInfo == null ? null :
-        new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
+    final EdmTypeInfo type = typeInfo == null ? null
+            : new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
 
     while (nodeItor.hasNext()) {
       final JsonNode child = nodeItor.next();
@@ -371,8 +384,8 @@ public class JsonDeserializer implements ODataDeserializer {
           ((ObjectNode) child).remove(jsonType);
         }
         final Object value = fromComplex((ObjectNode) child, codec);
-        valueType = value instanceof LinkedComplexValue ? ValueType.COLLECTION_LINKED_COMPLEX :
-            ValueType.COLLECTION_COMPLEX;
+        valueType = value instanceof LinkedComplexValue ? ValueType.COLLECTION_LINKED_COMPLEX
+                : ValueType.COLLECTION_COMPLEX;
         values.add(value);
       }
     }
@@ -380,50 +393,51 @@ public class JsonDeserializer implements ODataDeserializer {
   }
 
   protected void value(final Valuable valuable, final JsonNode node, final ObjectCodec codec)
-      throws IOException, EdmPrimitiveTypeException {
-    EdmTypeInfo typeInfo = StringUtils.isBlank(valuable.getType()) ? null :
-        new EdmTypeInfo.Builder().setTypeExpression(valuable.getType()).build();
+          throws IOException, EdmPrimitiveTypeException {
+    
+    EdmTypeInfo typeInfo = StringUtils.isBlank(valuable.getType()) ? null
+            : new EdmTypeInfo.Builder().setTypeExpression(valuable.getType()).build();
 
     final Map.Entry<ODataPropertyType, EdmTypeInfo> guessed = guessPropertyType(node);
     if (typeInfo == null) {
       typeInfo = guessed.getValue();
     }
 
-    final ODataPropertyType propType = typeInfo == null ? guessed.getKey() :
-        typeInfo.isCollection() ? ODataPropertyType.COLLECTION :
-            typeInfo.isPrimitiveType() ? ODataPropertyType.PRIMITIVE :
-                node.isValueNode() ? ODataPropertyType.ENUM : ODataPropertyType.COMPLEX;
+    final ODataPropertyType propType = typeInfo == null ? guessed.getKey()
+            : typeInfo.isCollection() ? ODataPropertyType.COLLECTION
+            : typeInfo.isPrimitiveType() ? ODataPropertyType.PRIMITIVE
+            : node.isValueNode() ? ODataPropertyType.ENUM : ODataPropertyType.COMPLEX;
 
     switch (propType) {
-    case COLLECTION:
-      fromCollection(valuable, node.elements(), typeInfo, codec);
-      break;
-
-    case COMPLEX:
-      if (node.has(jsonType)) {
-        valuable.setType(node.get(jsonType).asText());
-        ((ObjectNode) node).remove(jsonType);
-      }
-      final Object value = fromComplex((ObjectNode) node, codec);
-      valuable.setValue(value instanceof LinkedComplexValue ? ValueType.LINKED_COMPLEX : ValueType.COMPLEX, value);
-      break;
+      case COLLECTION:
+        fromCollection(valuable, node.elements(), typeInfo, codec);
+        break;
+
+      case COMPLEX:
+        if (node.has(jsonType)) {
+          valuable.setType(node.get(jsonType).asText());
+          ((ObjectNode) node).remove(jsonType);
+        }
+        final Object value = fromComplex((ObjectNode) node, codec);
+        valuable.setValue(value instanceof LinkedComplexValue ? ValueType.LINKED_COMPLEX : ValueType.COMPLEX, value);
+        break;
 
-    case ENUM:
-      valuable.setValue(ValueType.ENUM, node.asText());
-      break;
+      case ENUM:
+        valuable.setValue(ValueType.ENUM, node.asText());
+        break;
 
-    case PRIMITIVE:
-      if (valuable.getType() == null && typeInfo != null) {
-        valuable.setType(typeInfo.getFullQualifiedName().toString());
-      }
-      final Object primitiveValue = fromPrimitive(node, typeInfo);
-      valuable.setValue(primitiveValue instanceof Geospatial ? ValueType.GEOSPATIAL : ValueType.PRIMITIVE,
-          primitiveValue);
-      break;
-
-    case EMPTY:
-    default:
-      valuable.setValue(ValueType.PRIMITIVE, StringUtils.EMPTY);
+      case PRIMITIVE:
+        if (valuable.getType() == null && typeInfo != null) {
+          valuable.setType(typeInfo.getFullQualifiedName().toString());
+        }
+        final Object primitiveValue = fromPrimitive(node, typeInfo);
+        valuable.setValue(primitiveValue instanceof Geospatial ? ValueType.GEOSPATIAL : ValueType.PRIMITIVE,
+                primitiveValue);
+        break;
+
+      case EMPTY:
+      default:
+        valuable.setValue(ValueType.PRIMITIVE, StringUtils.EMPTY);
     }
   }