diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java index 2263e9b47..15c8c3e62 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java @@ -18,10 +18,17 @@ */ package org.apache.olingo.fit.tecsvc.client; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.net.URI; +import java.util.Calendar; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -30,32 +37,34 @@ import java.util.concurrent.TimeoutException; import org.apache.olingo.client.api.ODataClient; import org.apache.olingo.client.api.communication.ODataClientErrorException; import org.apache.olingo.client.api.communication.request.AsyncBatchRequestWrapper; +import org.apache.olingo.client.api.communication.request.AsyncRequestWrapper; import org.apache.olingo.client.api.communication.request.ODataBatchableRequest; import org.apache.olingo.client.api.communication.request.ODataRequest; import org.apache.olingo.client.api.communication.request.batch.ODataBatchRequest; import org.apache.olingo.client.api.communication.request.batch.ODataBatchResponseItem; import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest; +import org.apache.olingo.client.api.communication.request.invoke.ODataInvokeRequest; import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest; import org.apache.olingo.client.api.communication.response.AsyncResponseWrapper; import org.apache.olingo.client.api.communication.response.ODataBatchResponse; import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse; +import org.apache.olingo.client.api.communication.response.ODataInvokeResponse; import org.apache.olingo.client.api.communication.response.ODataResponse; import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; import org.apache.olingo.client.api.data.ResWrap; import org.apache.olingo.client.api.domain.ClientEntity; import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.domain.ClientProperty; +import org.apache.olingo.client.api.domain.ClientValue; import org.apache.olingo.client.api.uri.URIBuilder; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.format.PreferenceName; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpStatusCode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.apache.olingo.fit.tecsvc.TecSvcConst; import org.junit.Test; public final class AsyncSupportITCase extends AbstractParamTecSvcITCase { @@ -275,7 +284,34 @@ public final class AsyncSupportITCase extends AbstractParamTecSvcITCase { assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), e.getStatusLine().getStatusCode()); } } + + @Test + public void entityAction() throws Exception { + Calendar dateTime = Calendar.getInstance(); + dateTime.clear(); + dateTime.set(1012, 2, 0, 0, 0, 0); + final Map parameters = Collections.singletonMap( + "ParameterDate", + (ClientValue) getFactory().newPrimitiveValueBuilder() + .setType(EdmPrimitiveTypeKind.Date).setValue(dateTime).build()); + ODataClient client = getClient(); + URI uri = client.newURIBuilder(TecSvcConst.BASE_URI) + .appendActionCallSegment("AIRTESAllPrimParam").build(); + ODataInvokeRequest req = client.getInvokeRequestFactory() + .getActionInvokeRequest(uri, ClientEntity.class, parameters); + AsyncRequestWrapper> + asyncReqWrp = client.getAsyncRequestFactory().getAsyncRequestWrapper(req); + AsyncResponseWrapper> + asyncRespWrp = asyncReqWrp.execute(); + waitTillDone(asyncRespWrp, 5); + @SuppressWarnings("unchecked") + ODataInvokeResponse response = (ODataInvokeResponse)asyncRespWrp.getODataResponse(); + + assertEquals(HttpStatusCode.CREATED.getStatusCode(), response.getStatusCode()); + assertEquals(TecSvcConst.BASE_URI + "/ESAllPrim(1)", response.getHeader(HttpHeader.LOCATION).iterator().next()); + } + private ODataEntityRequest appendGetRequest(final ODataClient client, final String segment, final Object key, final boolean isRelative) { final URI targetURI = client.newURIBuilder(SERVICE_URI) diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/AbstractODataInvokeRequest.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/AbstractODataInvokeRequest.java index 8bd33637a..5cf938540 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/AbstractODataInvokeRequest.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/AbstractODataInvokeRequest.java @@ -55,7 +55,7 @@ public abstract class AbstractODataInvokeRequest extends AbstractODataBasicRequest> implements ODataInvokeRequest, ODataBatchableRequest { - private final Class reference; + protected final Class reference; /** * Function parameters. diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/ODataInvokeRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/ODataInvokeRequestImpl.java index 6d6cc01e5..d00f97751 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/ODataInvokeRequestImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/invoke/ODataInvokeRequestImpl.java @@ -18,10 +18,22 @@ */ package org.apache.olingo.client.core.communication.request.invoke; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.communication.request.invoke.ClientNoContent; +import org.apache.olingo.client.api.communication.response.ODataInvokeResponse; +import org.apache.olingo.client.api.domain.ClientEntity; +import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.domain.ClientInvokeResult; +import org.apache.olingo.client.api.domain.ClientProperty; +import org.apache.olingo.client.api.http.HttpClientException; +import org.apache.olingo.client.api.serialization.ODataDeserializerException; +import org.apache.olingo.client.core.communication.response.AbstractODataResponse; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpMethod; @@ -45,4 +57,52 @@ public class ODataInvokeRequestImpl extends Abstra protected ContentType getPOSTParameterFormat() { return contentType == null ? getDefaultFormat() : contentType; } + + /** + * Response class about an ODataInvokeRequest. + */ + protected class ODataInvokeResponseImpl extends AbstractODataResponse implements ODataInvokeResponse { + + private T invokeResult = null; + + private ODataInvokeResponseImpl(final ODataClient odataClient, final HttpClient httpClient, + final HttpResponse res) { + + super(odataClient, httpClient, res); + } + + /** + * {@inheritDoc } + */ + @Override + public T getBody() { + if (invokeResult == null) { + try { + if (ClientNoContent.class.isAssignableFrom(reference)) { + invokeResult = reference.cast(new ClientNoContent()); + } else { + // avoid getContent() twice:IllegalStateException: Content has been consumed + final InputStream responseStream = this.payload == null ? res.getEntity().getContent() : this.payload; + if (ClientEntitySet.class.isAssignableFrom(reference)) { + invokeResult = reference.cast(odataClient.getReader().readEntitySet(responseStream, + ContentType.parse(getContentType()))); + } else if (ClientEntity.class.isAssignableFrom(reference)) { + invokeResult = reference.cast(odataClient.getReader().readEntity(responseStream, + ContentType.parse(getContentType()))); + } else if (ClientProperty.class.isAssignableFrom(reference)) { + invokeResult = reference.cast(odataClient.getReader().readProperty(responseStream, + ContentType.parse(getContentType()))); + } + } + } catch (IOException e) { + throw new HttpClientException(e); + } catch (final ODataDeserializerException e) { + throw new IllegalArgumentException(e); + } finally { + this.close(); + } + } + return invokeResult; + } + } } diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/edm/xml/annotation/ClientCsdlDynamicExpression.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/edm/xml/annotation/ClientCsdlDynamicExpression.java index 8e41a642c..1afbca510 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/edm/xml/annotation/ClientCsdlDynamicExpression.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/edm/xml/annotation/ClientCsdlDynamicExpression.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.Serializable; import org.apache.olingo.client.core.edm.xml.AbstractClientCsdlEdmDeserializer; +import org.apache.olingo.commons.api.edm.provider.annotation.CsdlAnnotationPath; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlDynamicExpression; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlExpression; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlIf; @@ -120,7 +121,7 @@ public abstract class ClientCsdlDynamicExpression extends CsdlDynamicExpression } else if (PROPERTY_PATH.equals(jp.getCurrentName())) { expression = new CsdlPropertyPath().setValue(jp.nextTextValue()); } else if (ANNOTATION_PATH.equals(jp.getCurrentName())) { - expression = new CsdlPath().setValue(jp.nextTextValue()); + expression = new CsdlAnnotationPath().setValue(jp.nextTextValue()); } else if (APPLY.equals(jp.getCurrentName())) { jp.nextToken(); expression = jp.readValueAs(ClientCsdlApply.class); diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/MetadataTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/MetadataTest.java index 23ef55ffc..00cd2ad52 100644 --- a/lib/client-core/src/test/java/org/apache/olingo/client/core/MetadataTest.java +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/MetadataTest.java @@ -60,6 +60,7 @@ import org.apache.olingo.commons.api.edm.provider.CsdlFunctionImport; import org.apache.olingo.commons.api.edm.provider.CsdlSchema; import org.apache.olingo.commons.api.edm.provider.CsdlSingleton; import org.apache.olingo.commons.api.edm.provider.CsdlTerm; +import org.apache.olingo.commons.api.edm.provider.annotation.CsdlAnnotationPath; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlApply; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlCollection; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlConstantExpression; @@ -457,6 +458,33 @@ public class MetadataTest extends AbstractTest { assertEquals("EnumMember", expression.getExpressionName()); } } + + @Test + public void readPropertyAnnotationsTest() { + List streams = new ArrayList(); + streams.add(getClass().getResourceAsStream("VOC_Core.xml")); + final Edm edm = client.getReader().readMetadata(getClass().getResourceAsStream("edmxWithCsdlAnnotationPath.xml"), + streams); + assertNotNull(edm); + + final EdmEntityType person = edm.getEntityType( + new FullQualifiedName("Microsoft.Exchange.Services.OData.Model", "Person")); + assertNotNull(person); + EdmProperty userName = (EdmProperty) person.getProperty("UserName"); + List userNameAnnotations = userName.getAnnotations(); + for (EdmAnnotation annotation : userNameAnnotations) { + EdmTerm term = annotation.getTerm(); + assertNotNull(term); + assertEquals("Permissions", term.getName()); + assertEquals("Org.OData.Core.V1.Permissions", + term.getFullQualifiedName().getFullQualifiedNameAsString()); + EdmExpression expression = annotation.getExpression(); + assertNotNull(expression); + assertTrue(expression.isDynamic()); + assertEquals("AnnotationPath", expression.asDynamic().getExpressionName()); + } + } + @Test public void testOLINGO1100() { final Edm edm = client.getReader().readMetadata(getClass().getResourceAsStream("olingo1100.xml")); diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/edmxWithCsdlAnnotationPath.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/edmxWithCsdlAnnotationPath.xml new file mode 100644 index 000000000..331bb1387 --- /dev/null +++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/edmxWithCsdlAnnotationPath.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java index 16cca295d..ad06b7c49 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java @@ -150,6 +150,15 @@ public abstract class OData { */ public abstract ODataDeserializer createDeserializer(ContentType contentType) throws DeserializerException; + /** + * Creates a new deserializer object for reading content in the specified format. + * Deserializers are used in Processor implementations. + * + * @param contentType any content type supported by Olingo (XML, JSON ...) + */ + public abstract ODataDeserializer createDeserializer(ContentType contentType, + final List versions) throws DeserializerException; + /** * Creates a new deserializer object for reading content in the specified format. * Deserializers are used in Processor implementations. @@ -160,6 +169,16 @@ public abstract class OData { public abstract ODataDeserializer createDeserializer(ContentType contentType, ServiceMetadata metadata) throws DeserializerException; + /** + * Creates a new deserializer object for reading content in the specified format. + * Deserializers are used in Processor implementations. + * + * @param contentType any content type supported by Olingo (XML, JSON ...) + * @param metadata ServiceMetada of the service + */ + public abstract ODataDeserializer createDeserializer(ContentType contentType, + ServiceMetadata metadata, final List versions) throws DeserializerException; + /** * Creates a primitive-type instance. * @param kind the kind of the primitive type diff --git a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java index 113eb03b7..0a6ce52f4 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java @@ -160,5 +160,17 @@ public class ODataNettyImpl extends ODataNetty { throws SerializerException { return odata.createEdmDeltaSerializer(contentType, versions); } + + @Override + public ODataDeserializer createDeserializer(ContentType contentType, List versions) + throws DeserializerException { + return odata.createDeserializer(contentType, versions); + } + + @Override + public ODataDeserializer createDeserializer(ContentType contentType, ServiceMetadata metadata, List versions) + throws DeserializerException { + return odata.createDeserializer(contentType, metadata, versions); + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java index c3507fb85..997fc8f17 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java @@ -238,4 +238,40 @@ public class ODataImpl extends OData { // TODO: Support more debug formats return new DebugResponseHelperImpl(debugFormat); } + + @Override + public ODataDeserializer createDeserializer(ContentType contentType, List versions) + throws DeserializerException { + IConstants constants = new Constantsv00(); + if(versions!=null && versions.size()>0 && getMaxVersion(versions)>4){ + constants = new Constantsv01() ; + } + if (contentType.isCompatible(ContentType.JSON)) { + return new ODataJsonDeserializer(contentType, constants); + } else if (contentType.isCompatible(ContentType.APPLICATION_XML) + || contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) { + return new ODataXmlDeserializer(); + } else { + throw new DeserializerException("Unsupported format: " + contentType.toContentTypeString(), + DeserializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString()); + } + } + + @Override + public ODataDeserializer createDeserializer(ContentType contentType, ServiceMetadata metadata, List versions) + throws DeserializerException { + IConstants constants = new Constantsv00(); + if(versions!=null && versions.size()>0 && getMaxVersion(versions)>4){ + constants = new Constantsv01() ; + } + if (contentType.isCompatible(ContentType.JSON)) { + return new ODataJsonDeserializer(contentType, metadata, constants); + } else if (contentType.isCompatible(ContentType.APPLICATION_XML) + || contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) { + return new ODataXmlDeserializer(metadata); + } else { + throw new DeserializerException("Unsupported format: " + contentType.toContentTypeString(), + DeserializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString()); + } + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java index 71aee422e..cb96c20d1 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java @@ -33,6 +33,8 @@ import java.util.Map; import java.util.Map.Entry; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.IConstants; +import org.apache.olingo.commons.api.constants.Constantsv00; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; @@ -105,14 +107,27 @@ public class ODataJsonDeserializer implements ODataDeserializer { private final boolean isIEEE754Compatible; private ServiceMetadata serviceMetadata; + private IConstants constants; public ODataJsonDeserializer(final ContentType contentType) { - this(contentType, null); + this(contentType, null, new Constantsv00()); } public ODataJsonDeserializer(final ContentType contentType, final ServiceMetadata serviceMetadata) { isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); this.serviceMetadata = serviceMetadata; + this.constants = new Constantsv00(); + } + + public ODataJsonDeserializer(ContentType contentType, ServiceMetadata serviceMetadata, IConstants constants) { + isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); + this.serviceMetadata = serviceMetadata; + this.constants = constants; + } + + public ODataJsonDeserializer(ContentType contentType, IConstants constants) { + isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); + this.constants = constants; } @Override @@ -337,7 +352,7 @@ public class ODataJsonDeserializer implements ODataDeserializer { while (fieldsIterator.hasNext()) { Entry field = fieldsIterator.next(); - if (field.getKey().contains(Constants.JSON_BIND_LINK_SUFFIX)) { + if (field.getKey().contains(constants.getBind())) { Link bindingLink = consumeBindingLink(field.getKey(), field.getValue(), edmEntityType); entity.getNavigationBindings().add(bindingLink); toRemove.add(field.getKey()); @@ -937,7 +952,7 @@ public class ODataJsonDeserializer implements ODataDeserializer { try { List parsedValues = new ArrayList(); final ObjectNode tree = parseJsonTree(stream); - final String key = Constants.JSON_ID; + final String key = constants.getId(); JsonNode jsonNode = tree.get(Constants.VALUE); if (jsonNode != null) { if (jsonNode.isArray()) { @@ -984,7 +999,7 @@ public class ODataJsonDeserializer implements ODataDeserializer { private EdmType getDerivedType(final EdmStructuredType edmType, final JsonNode jsonNode) throws DeserializerException { - JsonNode odataTypeNode = jsonNode.get(Constants.JSON_TYPE); + JsonNode odataTypeNode = jsonNode.get(constants.getType()); if (odataTypeNode != null) { String odataType = odataTypeNode.asText(); if (!odataType.isEmpty()) { diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java index baf2f40af..07a793a42 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java @@ -140,13 +140,13 @@ public class RequestValidator { if (edmProperty.isCollection()) { final EntityCollection inlineEntitySet = navigationLink.getInlineEntitySet(); if (inlineEntitySet != null) { - if (!isInsert && inlineEntitySet.getEntities().size() > 0) { + /*if (!isInsert && inlineEntitySet.getEntities().size() > 0) { throw new DataProvider.DataProviderException("Deep update is not allowed", HttpStatusCode.BAD_REQUEST); - } else { + } else {*/ for (final Entity entity : navigationLink.getInlineEntitySet().getEntities()) { validate(edmBindingTarget, entity); } - } + // } } } else { final Entity inlineEntity = navigationLink.getInlineEntity();