From c953fca19d1db3207b69f48bdf37f3ebf33e5ea7 Mon Sep 17 00:00:00 2001 From: fmartelli Date: Wed, 16 Apr 2014 13:07:56 +0200 Subject: [PATCH] [OLINGO-175, OLINGO-205, OLINGO-246] provied V3 integration test for batch features including references in changeset --- .../apache/olingo/fit/AbstractServices.java | 174 +++++++++++++----- .../org/apache/olingo/fit/V3KeyAsSegment.java | 17 +- .../org/apache/olingo/fit/V3OpenType.java | 9 +- .../org/apache/olingo/fit/V3Services.java | 1 - .../org/apache/olingo/fit/V4OpenType.java | 12 +- .../org/apache/olingo/fit/V4Services.java | 22 ++- .../olingo/fit/utils/AbstractUtilities.java | 27 ++- .../V30/CustomerInfo/17/entity.full.json | 11 ++ .../resources/V30/CustomerInfo/17/entity.xml | 37 ++++ .../client/core/it/v3/BatchTestITCase.java | 7 +- 10 files changed, 239 insertions(+), 78 deletions(-) create mode 100644 fit/src/main/resources/V30/CustomerInfo/17/entity.full.json create mode 100644 fit/src/main/resources/V30/CustomerInfo/17/entity.xml diff --git a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java index 08373f626..5ca2048e6 100644 --- a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java +++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java @@ -114,6 +114,8 @@ public abstract class AbstractServices { private static final Pattern REQUEST_PATTERN = Pattern.compile("(.*) (http://.*) HTTP/.*"); + private static final Pattern BATCH_REQUEST_REF_PATTERN = Pattern.compile("(.*) ([$].*) HTTP/.*"); + private static final String BOUNDARY = "batch_243234_25424_ef_892u748"; protected final ODataServiceVersion version; @@ -161,6 +163,7 @@ public abstract class AbstractServices { } return xml.createResponse( + null, FSManager.instance(version).readFile(Constants.get(version, ConstantKey.SERVICES), acceptType), null, acceptType); } catch (Exception e) { @@ -182,7 +185,7 @@ public abstract class AbstractServices { protected Response getMetadata(final String filename) { try { - return xml.createResponse(FSManager.instance(version).readFile(filename, Accept.XML), null, Accept.XML); + return xml.createResponse(null, FSManager.instance(version).readFile(filename, Accept.XML), null, Accept.XML); } catch (Exception e) { return xml.createFaultResponse(Accept.XML.toString(version), e); } @@ -201,22 +204,29 @@ public abstract class AbstractServices { } private Response bodyPartRequest(final MimeBodyPart body) throws Exception { + return bodyPartRequest(body, Collections.emptyMap()); + } + + private Response bodyPartRequest(final MimeBodyPart body, final Map references) throws Exception { @SuppressWarnings("unchecked") final Enumeration
en = (Enumeration
) body.getAllHeaders(); Header header = en.nextElement(); - final String request = header.getName() + ":" + header.getValue(); + final String request = + header.getName() + (StringUtils.isNotBlank(header.getValue()) ? ":" + header.getValue() : ""); + final Matcher matcher = REQUEST_PATTERN.matcher(request); + final Matcher matcherRef = BATCH_REQUEST_REF_PATTERN.matcher(request); + + final MultivaluedMap headers = new MultivaluedHashMap(); + + while (en.hasMoreElements()) { + header = en.nextElement(); + headers.putSingle(header.getName(), header.getValue()); + } if (matcher.find()) { - final MultivaluedMap headers = new MultivaluedHashMap(); - - while (en.hasMoreElements()) { - header = en.nextElement(); - headers.putSingle(header.getName(), header.getValue()); - } - String method = matcher.group(1); if ("PATCH".equals(method) || "MERGE".equals(method)) { headers.putSingle("X-HTTP-METHOD", method); @@ -226,7 +236,19 @@ public abstract class AbstractServices { final String url = matcher.group(2); final WebClient client = WebClient.create(url); + client.headers(headers); + return client.invoke(method, body.getDataHandler().getInputStream()); + } else if (matcherRef.find()) { + String method = matcherRef.group(1); + if ("PATCH".equals(method) || "MERGE".equals(method)) { + headers.putSingle("X-HTTP-METHOD", method); + method = "POST"; + } + + final String url = matcherRef.group(2); + + final WebClient client = WebClient.create(references.get(url)); client.headers(headers); return client.invoke(method, body.getDataHandler().getInputStream()); @@ -246,6 +268,8 @@ public abstract class AbstractServices { final Object content = obj.getDataHandler().getContent(); if (content instanceof MimeMultipart) { + final Map references = new HashMap(); + final String cboundary = "changeset_" + UUID.randomUUID().toString(); bos.write(("Content-Type: multipart/mixed;boundary=" + cboundary).getBytes()); bos.write(Constants.CRLF); @@ -259,12 +283,13 @@ public abstract class AbstractServices { lastContebtID = part.getContentID(); addChangesetItemIntro(chbos, lastContebtID, cboundary); - res = bodyPartRequest(new MimeBodyPart(part.getInputStream())); - if (res.getStatus() > 400) { + res = bodyPartRequest(new MimeBodyPart(part.getInputStream()), references); + if (res.getStatus() >= 400) { throw new Exception("Failure processing changeset"); } addSingleBatchResponse(res, lastContebtID, chbos); + references.put("$" + lastContebtID, res.getHeaderString("Location")); } bos.write(chbos.toByteArray()); IOUtils.closeQuietly(chbos); @@ -290,7 +315,7 @@ public abstract class AbstractServices { res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream())); - if (res.getStatus() > 400) { + if (res.getStatus() >= 400) { throw new Exception("Failure processing changeset"); } @@ -384,6 +409,7 @@ public abstract class AbstractServices { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response mergeEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -392,7 +418,7 @@ public abstract class AbstractServices { @PathParam("entityId") String entityId, final String changes) { - return patchEntity(accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); + return patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); } @PATCH @@ -400,6 +426,7 @@ public abstract class AbstractServices { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response patchEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -442,11 +469,16 @@ public abstract class AbstractServices { final Response response; if ("return-content".equalsIgnoreCase(prefer)) { response = xml.createResponse( + uriInfo.getRequestUri().toASCIIString(), util.readEntity(entitySetName, entityId, acceptType).getValue(), null, acceptType, Response.Status.OK); } else { res.close(); - response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + response = xml.createResponse( + uriInfo.getRequestUri().toASCIIString(), + null, + null, + acceptType, Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -464,6 +496,7 @@ public abstract class AbstractServices { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response replaceEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -504,11 +537,19 @@ public abstract class AbstractServices { final Response response; if ("return-content".equalsIgnoreCase(prefer)) { response = xml.createResponse( + uriInfo.getRequestUri().toASCIIString(), getUtilities(acceptType).readEntity(entitySetName, entityId, acceptType).getValue(), - null, acceptType, Response.Status.OK); + null, + acceptType, + Response.Status.OK); } else { res.close(); - response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + response = xml.createResponse( + uriInfo.getRequestUri().toASCIIString(), + null, + null, + acceptType, + Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -526,6 +567,7 @@ public abstract class AbstractServices { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM}) public Response postNewEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -591,7 +633,7 @@ public abstract class AbstractServices { } else { final Container jcontainer = mapper.readValue(IOUtils.toInputStream(entity), new TypeReference() { - }); + }); entry = (new DataBinder(version)). getAtomEntry(jcontainer.getObject()); @@ -620,12 +662,23 @@ public abstract class AbstractServices { FSManager.instance(version).putInMemory( cres, path + File.separatorChar + Constants.get(version, ConstantKey.ENTITY)); + final String location = uriInfo.getRequestUri().toASCIIString() + "(" + entityKey + ")"; + final Response response; if ("return-no-content".equalsIgnoreCase(prefer)) { - response = utils.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + response = utils.createResponse( + location, + null, + null, + acceptType, + Response.Status.NO_CONTENT); } else { - response = utils.createResponse(utils.readEntity(entitySetName, entityKey, acceptType).getValue(), - null, acceptType, Response.Status.CREATED); + response = utils.createResponse( + location, + utils.readEntity(entitySetName, entityKey, acceptType).getValue(), + null, + acceptType, + Response.Status.CREATED); } if (StringUtils.isNotBlank(prefer)) { @@ -664,15 +717,15 @@ public abstract class AbstractServices { replaceAll("\"Salary\":[0-9]*,", "\"Salary\":0,"). replaceAll("\"Title\":\".*\"", "\"Title\":\"[Sacked]\""). replaceAll("\\.*\\", - "0"). + "0"). replaceAll("\\.*\\", "[Sacked]"); final FSManager fsManager = FSManager.instance(version); fsManager.putInMemory(IOUtils.toInputStream(newContent, "UTF-8"), fsManager.getAbsolutePath(Commons.getEntityBasePath("Person", entityId) + Constants.get(version, - ConstantKey.ENTITY), utils.getKey())); + ConstantKey.ENTITY), utils.getKey())); - return utils.getValue().createResponse(null, null, utils.getKey(), Response.Status.NO_CONTENT); + return utils.getValue().createResponse(null, null, null, utils.getKey(), Response.Status.NO_CONTENT); } catch (Exception e) { return xml.createFaultResponse(accept, e); } @@ -722,15 +775,15 @@ public abstract class AbstractServices { final Long newSalary = Long.valueOf(salaryMatcher.group(1)) + n; newContent = newContent. replaceAll("\"Salary\":" + salaryMatcher.group(1) + ",", - "\"Salary\":" + newSalary + ","). + "\"Salary\":" + newSalary + ","). replaceAll("\\" + salaryMatcher.group(1) + "", - "" + newSalary + ""); + "" + newSalary + ""); } FSManager.instance(version).putInMemory(IOUtils.toInputStream(newContent, "UTF-8"), FSManager.instance(version).getAbsolutePath(path.toString(), acceptType)); - return xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + return xml.createResponse(null, null, null, acceptType, Response.Status.NO_CONTENT); } catch (Exception e) { return xml.createFaultResponse(accept, e); } @@ -767,7 +820,7 @@ public abstract class AbstractServices { : Constants.get(version, ConstantKey.FEED)); final InputStream feed = FSManager.instance(version).readFile(path.toString(), acceptType); - return xml.createResponse(feed, Commons.getETag(basePath, version), acceptType); + return xml.createResponse(null, feed, Commons.getETag(basePath, version), acceptType); } catch (Exception e) { return xml.createFaultResponse(accept, e); } @@ -788,6 +841,7 @@ public abstract class AbstractServices { @GET @Path("/{name}") public Response getEntitySet( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("name") String name, @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format, @@ -804,10 +858,11 @@ public abstract class AbstractServices { acceptType = Accept.parse(accept, version); } + final String location = uriInfo.getRequestUri().toASCIIString(); try { // search for function ... final InputStream func = FSManager.instance(version).readFile(name, acceptType); - return xml.createResponse(func, null, acceptType); + return xml.createResponse(location, func, null, acceptType); } catch (NotFoundException e) { if (acceptType == Accept.XML || acceptType == Accept.TEXT) { throw new UnsupportedMediaTypeException("Unsupported media type"); @@ -856,11 +911,14 @@ public abstract class AbstractServices { mapper.writeValue( writer, new JsonFeedContainer(container.getContextURL(), container.getMetadataETag(), - new DataBinder(version).getJsonFeed(container.getObject()))); + new DataBinder(version).getJsonFeed(container.getObject()))); } - return xml.createResponse(new ByteArrayInputStream(content.toByteArray()), - Commons.getETag(basePath, version), acceptType); + return xml.createResponse( + location, + new ByteArrayInputStream(content.toByteArray()), + Commons.getETag(basePath, version), + acceptType); } } catch (Exception e) { return xml.createFaultResponse(accept, e); @@ -880,6 +938,7 @@ public abstract class AbstractServices { @GET @Path("/Person({entityId})") public Response getEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept, @PathParam("entityId") final String entityId, @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) { @@ -901,7 +960,10 @@ public abstract class AbstractServices { } return utils.getValue().createResponse( - entity, Commons.getETag(entityInfo.getKey(), version), utils.getKey()); + uriInfo.getRequestUri().toASCIIString(), + entity, + Commons.getETag(entityInfo.getKey(), version), + utils.getKey()); } catch (Exception e) { LOG.error("Error retrieving entity", e); return xml.createFaultResponse(accept, e); @@ -922,6 +984,7 @@ public abstract class AbstractServices { @GET @Path("/{entitySetName}({entityId})") public Response getEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @@ -929,10 +992,12 @@ public abstract class AbstractServices { @QueryParam("$expand") @DefaultValue(StringUtils.EMPTY) String expand, @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) { - return getEntityInternal(accept, entitySetName, entityId, format, expand, select, false); + return getEntityInternal( + uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, entityId, format, expand, select, false); } protected Response getEntityInternal( + final String location, final String accept, final String entitySetName, final String entityId, @@ -1038,11 +1103,14 @@ public abstract class AbstractServices { final ObjectMapper mapper = Commons.getJsonMapper(version); mapper.writeValue( writer, new JsonEntryContainer(container.getContextURL(), container.getMetadataETag(), - (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject()))); + (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject()))); } - return xml.createResponse(new ByteArrayInputStream(content.toByteArray()), - Commons.getETag(entityInfo.getKey(), version), utils.getKey()); + return xml.createResponse( + location, + new ByteArrayInputStream(content.toByteArray()), + Commons.getETag(entityInfo.getKey(), version), + utils.getKey()); } catch (Exception e) { LOG.error("Error retrieving entity", e); @@ -1053,6 +1121,7 @@ public abstract class AbstractServices { @GET @Path("/{entitySetName}({entityId})/$value") public Response getMediaEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId) { @@ -1064,7 +1133,11 @@ public abstract class AbstractServices { final AbstractUtilities utils = getUtilities(null); final Map.Entry entityInfo = utils.readMediaEntity(entitySetName, entityId); - return utils.createResponse(entityInfo.getValue(), Commons.getETag(entityInfo.getKey(), version), null); + return utils.createResponse( + uriInfo.getRequestUri().toASCIIString(), + entityInfo.getValue(), + Commons.getETag(entityInfo.getKey(), version), + null); } catch (Exception e) { LOG.error("Error retrieving entity", e); @@ -1084,7 +1157,7 @@ public abstract class AbstractServices { FSManager.instance(version).deleteFile(basePath + Constants.get(version, ConstantKey.ENTITY)); - return xml.createResponse(null, null, null, Response.Status.NO_CONTENT); + return xml.createResponse(null, null, null, null, Response.Status.NO_CONTENT); } catch (Exception e) { return xml.createFaultResponse(Accept.XML.toString(version), e); } @@ -1122,10 +1195,10 @@ public abstract class AbstractServices { final Response response; if ("return-content".equalsIgnoreCase(prefer)) { - response = xml.createResponse(changed, null, acceptType, Response.Status.OK); + response = xml.createResponse(null, changed, null, acceptType, Response.Status.OK); } else { changed.close(); - response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + response = xml.createResponse(null, null, null, acceptType, Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -1167,10 +1240,10 @@ public abstract class AbstractServices { final Response response; if ("return-content".equalsIgnoreCase(prefer)) { - response = xml.createResponse(changed, null, acceptType, Response.Status.OK); + response = xml.createResponse(null, changed, null, acceptType, Response.Status.OK); } else { changed.close(); - response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT); + response = xml.createResponse(null, null, null, acceptType, Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -1261,6 +1334,7 @@ public abstract class AbstractServices { @Consumes({MediaType.WILDCARD, MediaType.APPLICATION_OCTET_STREAM}) @Path("/{entitySetName}({entityId})/$value") public Response replaceMediaEntity( + @Context UriInfo uriInfo, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @@ -1272,12 +1346,14 @@ public abstract class AbstractServices { InputStream res = utils.putMediaInMemory(entitySetName, entityId, IOUtils.toInputStream(value)); + final String location = uriInfo.getRequestUri().toASCIIString().replace("/$value", ""); + final Response response; if ("return-content".equalsIgnoreCase(prefer)) { - response = xml.createResponse(res, null, null, Response.Status.OK); + response = xml.createResponse(location, res, null, null, Response.Status.OK); } else { res.close(); - response = xml.createResponse(null, null, null, Response.Status.NO_CONTENT); + response = xml.createResponse(location, null, null, null, Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -1333,10 +1409,10 @@ public abstract class AbstractServices { final Response response; if ("return-content".equalsIgnoreCase(prefer)) { - response = xml.createResponse(res, null, null, Response.Status.OK); + response = xml.createResponse(null, res, null, null, Response.Status.OK); } else { res.close(); - response = xml.createResponse(null, null, null, Response.Status.NO_CONTENT); + response = xml.createResponse(null, null, null, null, Response.Status.NO_CONTENT); } if (StringUtils.isNotBlank(prefer)) { @@ -1462,7 +1538,7 @@ public abstract class AbstractServices { final AbstractUtilities utils = getUtilities(null); final Map.Entry entityInfo = utils.readMediaEntity(entitySetName, entityId, path); - return utils.createResponse(entityInfo.getValue(), Commons.getETag(entityInfo.getKey(), version), null); + return utils.createResponse(null, entityInfo.getValue(), Commons.getETag(entityInfo.getKey(), version), null); } private Response navigateEntity( @@ -1511,7 +1587,7 @@ public abstract class AbstractServices { } } final String basePath = Commons.getEntityBasePath(entitySetName, entityId); - return xml.createResponse(stream, Commons.getETag(basePath, version), acceptType); + return xml.createResponse(null, stream, Commons.getETag(basePath, version), acceptType); } private Response navigateProperty( @@ -1544,7 +1620,7 @@ public abstract class AbstractServices { stream = utils.getProperty(entitySetName, entityId, pathElements, edmType); } - return xml.createResponse(stream, Commons.getETag(basePath, version), acceptType); + return xml.createResponse(null, stream, Commons.getETag(basePath, version), acceptType); } /** diff --git a/fit/src/main/java/org/apache/olingo/fit/V3KeyAsSegment.java b/fit/src/main/java/org/apache/olingo/fit/V3KeyAsSegment.java index 01572ab2f..da4a7db6a 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V3KeyAsSegment.java +++ b/fit/src/main/java/org/apache/olingo/fit/V3KeyAsSegment.java @@ -32,8 +32,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.olingo.fit.methods.MERGE; @@ -79,6 +81,7 @@ public class V3KeyAsSegment { @GET @Path("/{entitySetName}/{entityId}") public Response getEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @@ -86,7 +89,7 @@ public class V3KeyAsSegment { @QueryParam("$expand") @DefaultValue(StringUtils.EMPTY) String expand, @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) { - return replaceServiceName(services.getEntityInternal( + return replaceServiceName(services.getEntityInternal(uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, entityId, format, expand, select, true)); } @@ -104,6 +107,7 @@ public class V3KeyAsSegment { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response mergeEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -113,7 +117,7 @@ public class V3KeyAsSegment { final String changes) { return replaceServiceName( - services.patchEntity(accept, contentType, prefer, ifMatch, entitySetName, entityId, changes)); + services.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes)); } @PATCH @@ -121,6 +125,7 @@ public class V3KeyAsSegment { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response patchEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -130,7 +135,7 @@ public class V3KeyAsSegment { final String changes) { return replaceServiceName( - services.patchEntity(accept, contentType, prefer, ifMatch, entitySetName, entityId, changes)); + services.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes)); } @PUT @@ -138,6 +143,7 @@ public class V3KeyAsSegment { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) public Response putNewEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @@ -146,7 +152,7 @@ public class V3KeyAsSegment { final String entity) { return replaceServiceName( - services.replaceEntity(accept, contentType, prefer, entitySetName, entityId, entity)); + services.replaceEntity(uriInfo, accept, contentType, prefer, entitySetName, entityId, entity)); } @POST @@ -154,12 +160,13 @@ public class V3KeyAsSegment { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM}) public Response postNewEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @PathParam("entitySetName") String entitySetName, final String entity) { - return replaceServiceName(services.postNewEntity(accept, contentType, prefer, entitySetName, entity)); + return replaceServiceName(services.postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entity)); } } diff --git a/fit/src/main/java/org/apache/olingo/fit/V3OpenType.java b/fit/src/main/java/org/apache/olingo/fit/V3OpenType.java index c75905076..18f5075b1 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V3OpenType.java +++ b/fit/src/main/java/org/apache/olingo/fit/V3OpenType.java @@ -33,8 +33,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; @@ -110,6 +112,7 @@ public class V3OpenType { @GET @Path("/{entitySetName}({entityId})") public Response getEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @@ -118,7 +121,8 @@ public class V3OpenType { @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) { final Matcher matcher = GUID.matcher(entityId); - return replaceServiceName(services.getEntityInternal(accept, entitySetName, + return replaceServiceName(services.getEntityInternal( + uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, matcher.matches() ? matcher.group(1) : entityId, format, expand, select, false)); } @@ -127,13 +131,14 @@ public class V3OpenType { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM}) public Response postNewEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @PathParam("entitySetName") final String entitySetName, final String entity) { - return replaceServiceName(services.postNewEntity(accept, contentType, prefer, entitySetName, entity)); + return replaceServiceName(services.postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entity)); } @DELETE diff --git a/fit/src/main/java/org/apache/olingo/fit/V3Services.java b/fit/src/main/java/org/apache/olingo/fit/V3Services.java index 9963684ca..c884af0f0 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V3Services.java +++ b/fit/src/main/java/org/apache/olingo/fit/V3Services.java @@ -319,5 +319,4 @@ public class V3Services extends AbstractServices { return xml.createFaultResponse(accept, e); } } - } diff --git a/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java b/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java index f7744f713..aff2f43b2 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java +++ b/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java @@ -31,8 +31,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; @@ -54,9 +56,8 @@ public class V4OpenType { public V4OpenType() throws Exception { this.openMetadata = new Metadata(FSManager.instance(ODataServiceVersion.V40). readFile("openType" + StringUtils.capitalize(Constants.get(ODataServiceVersion.V40, ConstantKey.METADATA)), - Accept.XML)); + Accept.XML)); this.services = new V4Services() { - @Override protected Metadata getMetadataObj() { return openMetadata; @@ -106,6 +107,7 @@ public class V4OpenType { @GET @Path("/{entitySetName}({entityId})") public Response getEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @@ -114,7 +116,8 @@ public class V4OpenType { @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) { return replaceServiceName( - services.getEntityInternal(accept, entitySetName, entityId, format, expand, select, false)); + services.getEntityInternal( + uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, entityId, format, expand, select, false)); } @POST @@ -122,13 +125,14 @@ public class V4OpenType { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM}) public Response postNewEntity( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer, @PathParam("entitySetName") final String entitySetName, final String entity) { - return replaceServiceName(services.postNewEntity(accept, contentType, prefer, entitySetName, entity)); + return replaceServiceName(services.postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entity)); } @DELETE diff --git a/fit/src/main/java/org/apache/olingo/fit/V4Services.java b/fit/src/main/java/org/apache/olingo/fit/V4Services.java index 875bee868..8de437b65 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V4Services.java +++ b/fit/src/main/java/org/apache/olingo/fit/V4Services.java @@ -31,6 +31,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -97,7 +98,7 @@ public class V4Services extends AbstractServices { return utils.getValue().createResponse( FSManager.instance(version).readFile(Constants.get(version, ConstantKey.REF) - + File.separatorChar + filename, utils.getKey()), + + File.separatorChar + filename, utils.getKey()), null, utils.getKey()); } catch (Exception e) { @@ -108,6 +109,7 @@ public class V4Services extends AbstractServices { @Override public Response patchEntity( + final UriInfo uriInfo, final String accept, final String contentType, final String prefer, @@ -117,14 +119,16 @@ public class V4Services extends AbstractServices { final String changes) { final Response response = - getEntityInternal(accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); + getEntityInternal(uriInfo.getRequestUri().toASCIIString(), + accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); return response.getStatus() >= 400 - ? postNewEntity(accept, contentType, prefer, entitySetName, changes) - : super.patchEntity(accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); + ? postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, changes) + : super.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); } @Override public Response replaceEntity( + final UriInfo uriInfo, final String accept, final String contentType, final String prefer, @@ -133,10 +137,11 @@ public class V4Services extends AbstractServices { final String entity) { try { - getEntityInternal(accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); - return super.replaceEntity(accept, contentType, prefer, entitySetName, entityId, entity); + getEntityInternal(uriInfo.getRequestUri().toASCIIString(), + accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); + return super.replaceEntity(uriInfo, accept, contentType, prefer, entitySetName, entityId, entity); } catch (NotFoundException e) { - return postNewEntity(accept, contentType, prefer, entitySetName, entityId); + return postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entityId); } } @@ -225,7 +230,7 @@ public class V4Services extends AbstractServices { final Container jsonContainer = mapper.readValue(IOUtils.toInputStream(changes), new TypeReference() { - }); + }); jsonContainer.getObject().setType(typeInfo.getFullQualifiedName().toString()); entryChanges = dataBinder.getAtomEntry(jsonContainer.getObject()); } @@ -246,5 +251,4 @@ public class V4Services extends AbstractServices { return xml.createFaultResponse(accept, e); } } - } diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java index 4a2af9345..251650b32 100644 --- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java +++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java @@ -365,7 +365,7 @@ public abstract class AbstractUtilities { fsManager.putInMemory( IOUtils.toInputStream(entity), fsManager.getAbsolutePath(path + Constants.get(version, ConstantKey.ENTITY), - Accept.JSON_FULLMETA)); + Accept.JSON_FULLMETA)); // ----------------------------------------- return readEntity(entitySetName, entityKey, getDefaultFormat()).getValue(); @@ -410,8 +410,12 @@ public abstract class AbstractUtilities { } public Response createResponse( - final InputStream entity, final String etag, final Accept accept) { - return createResponse(entity, etag, accept, null); + final String location, final InputStream entity, final String etag, final Accept accept) { + return createResponse(location, entity, etag, accept, null); + } + + public Response createResponse(final InputStream entity, final String etag, final Accept accept) { + return createResponse(null, entity, etag, accept, null); } public Response createBatchResponse(final InputStream stream, final String boundary) { @@ -421,7 +425,18 @@ public abstract class AbstractUtilities { } public Response createResponse( - final InputStream entity, final String etag, final Accept accept, final Response.Status status) { + final InputStream entity, + final String etag, + final Accept accept, + final Response.Status status) { + return createResponse(null, entity, etag, accept, status); + } + public Response createResponse( + final String location, + final InputStream entity, + final String etag, + final Accept accept, + final Response.Status status) { final Response.ResponseBuilder builder = Response.ok(); if (version.compareTo(ODataServiceVersion.V30) <= 0) { @@ -466,6 +481,10 @@ public abstract class AbstractUtilities { builder.header("Content-Length", contentLength); builder.header("Content-Type", (accept == null ? "*/*" : accept.toString(version)) + contentTypeEncoding); + if (StringUtils.isNotBlank(location)) { + builder.header("Location", location); + } + return builder.build(); } diff --git a/fit/src/main/resources/V30/CustomerInfo/17/entity.full.json b/fit/src/main/resources/V30/CustomerInfo/17/entity.full.json new file mode 100644 index 000000000..3b4075ba2 --- /dev/null +++ b/fit/src/main/resources/V30/CustomerInfo/17/entity.full.json @@ -0,0 +1,11 @@ +{ + "odata.metadata": "http://localhost:${cargo.servlet.port}/StaticService/V30/Static.svc/$metadata#CustomerInfo/@Element", + "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.CustomerInfo", + "odata.id": "http://localhost:${cargo.servlet.port}/StaticService/V30/Static.svc/CustomerInfo(17)", + "odata.editLink": "CustomerInfo(17)", + "odata.mediaEditLink": "CustomerInfo(17)/$value", + "odata.mediaReadLink": "CustomerInfo(17)/$value", + "odata.mediaContentType": "*/*", + "CustomerInfoId": 17, + "Information": "び黑ポ畚ぜマチンハ歹黑zクヲネボァたグヲ黑ソЯ歹ぴせポzゼ弌ぞせぜゼ亜Яクあソ亜ゼそせ珱ァタひグゼ縷яぁゾ黑マミ裹暦ポя" +} \ No newline at end of file diff --git a/fit/src/main/resources/V30/CustomerInfo/17/entity.xml b/fit/src/main/resources/V30/CustomerInfo/17/entity.xml new file mode 100644 index 000000000..0feee002e --- /dev/null +++ b/fit/src/main/resources/V30/CustomerInfo/17/entity.xml @@ -0,0 +1,37 @@ + + + + http://localhost:${cargo.servlet.port}/StaticService/V30/Static.svc/CustomerInfo(17) + + + + <updated>2014-03-04T09:52:15Z</updated> + <author> + <name /> + </author> + <link rel="edit-media" title="CustomerInfo" href="CustomerInfo(17)/$value" /> + <content type="*/*" src="CustomerInfo(17)/$value" /> + <m:properties> + <d:CustomerInfoId m:type="Edm.Int32">17</d:CustomerInfoId> + <d:Information>び黑ポ畚ぜマチンハ歹黑zクヲネボァたグヲ黑ソЯ歹ぴせポzゼ弌ぞせぜゼ亜Яクあソ亜ゼそせ珱ァタひグゼ縷яぁゾ黑マミ裹暦ポя</d:Information> + </m:properties> +</entry> \ No newline at end of file diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java index 2e4e0df83..4213b6f06 100644 --- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java @@ -151,7 +151,6 @@ public class BatchTestITCase extends AbstractTestITCase { } @Test - @Ignore @SuppressWarnings("unchecked") public void changesetWithReference() throws EdmPrimitiveTypeException { // create your request @@ -161,7 +160,7 @@ public class BatchTestITCase extends AbstractTestITCase { final ODataChangeset changeset = streamManager.addChangeset(); ODataEntity customer = getSampleCustomerProfile(20, "sample customer", false); - URIBuilder uriBuilder = client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("Customer"); + URIBuilder uriBuilder = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer"); // add create request final ODataEntityCreateRequest<ODataEntity> createReq = @@ -176,10 +175,10 @@ public class BatchTestITCase extends AbstractTestITCase { final ODataEntity customerChanges = client.getObjectFactory().newEntity(customer.getTypeName()); customerChanges.addLink(client.getObjectFactory().newEntityNavigationLink( "Info", - client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("CustomerInfo"). + client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("CustomerInfo"). appendKeySegment(17).build())); - final ODataEntityUpdateRequest updateReq = client.getCUDRequestFactory().getEntityUpdateRequest( + final ODataEntityUpdateRequest<ODataEntity> updateReq = client.getCUDRequestFactory().getEntityUpdateRequest( URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges); changeset.addRequest(updateReq);