[OLINGO-362] Now supporting refresh token

This commit is contained in:
Francesco Chicchiriccò 2014-07-21 13:33:34 +02:00
parent df7cba6909
commit c155238d9c
14 changed files with 358 additions and 259 deletions

View File

@ -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 {

View File

@ -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);
}
}
}
}
}

View File

@ -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;

View File

@ -16,38 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.client.core.http;
package org.apache.olingo.fit.rest;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.olingo.client.api.http.HttpMethod;
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;
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);
@Provider
public class OAuth2RequestFilter extends OAuthRequestFilter implements ContainerRequestFilter {
@Override
public HttpUriRequest create(final HttpMethod method, final URI uri) {
if (!isInited()) {
init();
public void filter(final ContainerRequestContext context) {
final String svcName =
StringUtils.substringBefore(StringUtils.substringAfter(context.getUriInfo().getPath(), "/"), "/");
if ("OAuth2.svc".equals(svcName)) {
super.filter(context);
}
final HttpUriRequest request = super.create(method, uri);
sign(request);
return request;
}
}

View File

@ -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"/>

View File

@ -20,70 +20,84 @@ 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.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.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.olingo.client.core.http.AbstractOAuth2HttpUriRequestFactory;
import org.apache.olingo.client.core.http.AbstractOAuth2HttpClientFactory;
import org.apache.olingo.client.core.http.OAuth2Exception;
import org.apache.olingo.fit.rest.OAuth2Provider;
import java.net.URI;
public class CXFOAuth2HttpClientFactory extends AbstractOAuth2HttpClientFactory {
public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequestFactory {
private static final OAuthClientUtils.Consumer OAUTH2_CONSUMER =
new OAuthClientUtils.Consumer(OAuth2Provider.CLIENT_ID, OAuth2Provider.CLIENT_SECRET);
private String code;
private ClientAccessToken accessToken;
public CXFOAuth2HttpUriRequestFactory(final URI redirectURI) {
super(redirectURI);
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() {
return code != null;
protected boolean isInited() throws OAuth2Exception {
return accessToken != null;
}
@Override
protected void init() throws OAuth2Exception {
// 1. Disable automatic redirects handling
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);
// 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);
// 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);
// 4. Pull out OAuth2 authorization data and "authenticity" cookie (CXF specific)
// 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");
@ -95,9 +109,10 @@ public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequest
throw new OAuth2Exception(e);
}
String code = null;
try {
// 5. Submit the HTTP form for allowing access to the application
location = new URIBuilder(oAuthAuthorizationData.get("replyTo").asText()).
// 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()).
@ -114,18 +129,53 @@ public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequest
throw new IllegalStateException("OAuth flow is broken");
}
// 6. Finally get the code value out of this last redirect
// 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 sign(final HttpUriRequest request) {
request.addHeader(OAuth2Provider.OAUTH2_CODE_HEADER, code);
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);
}
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -28,12 +28,13 @@ 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 ProxyWrapperHttpClientFactory implements HttpClientFactory {
public class ProxyWrappingHttpClientFactory implements WrappingHttpClientFactory {
private final URI proxy;
@ -43,19 +44,19 @@ public class ProxyWrapperHttpClientFactory implements HttpClientFactory {
private final DefaultHttpClientFactory wrapped;
public ProxyWrapperHttpClientFactory(final URI proxy) {
public ProxyWrappingHttpClientFactory(final URI proxy) {
this(proxy, null, null, new DefaultHttpClientFactory());
}
public ProxyWrapperHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) {
public ProxyWrappingHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) {
this(proxy, proxyUsername, proxyPassword, new DefaultHttpClientFactory());
}
public ProxyWrapperHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) {
public ProxyWrappingHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) {
this(proxy, null, null, wrapped);
}
public ProxyWrapperHttpClientFactory(final URI proxy,
public ProxyWrappingHttpClientFactory(final URI proxy,
final String proxyUsername, final String proxyPassword, final DefaultHttpClientFactory wrapped) {
this.proxy = proxy;

View File

@ -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();

View File

@ -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;
}

View File

@ -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 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 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 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 EMPTY:
default:
valuable.setValue(ValueType.PRIMITIVE, StringUtils.EMPTY);
}
}