[OLINGO-1155]Delta support in Json format

This commit is contained in:
Archana Rai 2017-07-31 16:13:11 +05:30
parent f6dd0deacc
commit f7e5a5c716
36 changed files with 3567 additions and 47 deletions

View File

@ -319,4 +319,18 @@ public interface Constants {
String ASSOCIATION_LINK_TYPE = ContentType.APPLICATION_XML.toContentTypeString();
String ENTITY_COLLECTION_BINDING_LINK_TYPE = ContentType.APPLICATION_XML.toContentTypeString();
String ENTITY_BINDING_LINK_TYPE = ContentType.APPLICATION_XML.toContentTypeString();
//For v4.01 Delta
String LINK = "/$link";
String DELETEDLINK = "/$deletedLink";
String DELTA = "/$delta";
String DELTAVALUE = "delta";
String AT = "@";
String DELETEDENTITY = "/$deletedEntity";
String DELTALINK = "@deltaLink";
String HASH = "#";
String REMOVED = "removed";
String ENTITY = "/$entity";
String REASON = "Reason";
}

View File

@ -23,7 +23,7 @@ import java.net.URI;
/**
* A deleted entity contains the reason for deletion and the id.
*/
public class DeletedEntity {
public class DeletedEntity extends Entity{
/**
* Reason of the removal from the list

View File

@ -40,7 +40,12 @@ public enum ODataServiceVersion {
/**
* OData Version 4.0
*/
V40("4.0");
V40("4.0"),
/**
* OData Version 4.01
*/
V401("4.01");
private static final Pattern DATASERVICEVERSIONPATTERN = Pattern.compile("(\\p{Digit}+\\.\\p{Digit}+)(:?;.*)?");
@ -57,7 +62,8 @@ public enum ODataServiceVersion {
return V10.toString().equals(possibleDataServiceVersion)
|| V20.toString().equals(possibleDataServiceVersion)
|| V30.toString().equals(possibleDataServiceVersion)
|| V40.toString().equals(possibleDataServiceVersion);
|| V40.toString().equals(possibleDataServiceVersion)
|| V401.toString().equals(possibleDataServiceVersion);
} else {
throw new IllegalArgumentException(version);
}

View File

@ -29,6 +29,7 @@ public enum PreferenceName {
INCLUDE_ANNOTATIONS("odata.include-annotations"),
MAX_PAGE_SIZE("odata.maxpagesize"),
TRACK_CHANGES("odata.track-changes"),
TRACK_CHANGES_PREF("track-changes"),
RETURN("return"),
RESPOND_ASYNC("respond-async"),
WAIT("wait"),

View File

@ -35,6 +35,7 @@ import org.apache.olingo.server.api.etag.ETagHelper;
import org.apache.olingo.server.api.etag.ServiceMetadataETagSupport;
import org.apache.olingo.server.api.prefer.Preferences;
import org.apache.olingo.server.api.serializer.EdmAssistedSerializer;
import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
@ -184,4 +185,13 @@ public abstract class OData {
*/
public abstract EdmAssistedSerializer createEdmAssistedSerializer(final ContentType contentType)
throws SerializerException;
/**
* Creates a new serializer object capable of working without EDM information
* for rendering delta content in the specified format.
* @param contentType a content type supported by Olingo
* @param version versions supported by Olingo
*/
public abstract EdmDeltaSerializer createEdmDeltaSerializer(final ContentType contentType,
final List<String> versions) throws SerializerException;
}

View File

@ -0,0 +1,40 @@
/*
* 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.server.api.serializer;
import org.apache.olingo.commons.api.data.Delta;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.server.api.ServiceMetadata;
public interface EdmDeltaSerializer {
/**
* Writes collection of delta-response into an InputStream.
* Information from the EDM is used in addition to information from the data and preferred,
* but the serializer works without any EDM information as well.
* Linked data is always written as expanded items (so closed reference loops have to be avoided).
* @param metadata metadata for the service
* @param referencedEntityType the {@link EdmEntityType} or <code>null</code> if not available
* @param delta the delta data as entity collection
* @param options options for the serializer
*/
SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType referencedEntityType,
Delta delta, EntityCollectionSerializerOptions options) throws SerializerException;
}

View File

@ -41,6 +41,8 @@ public class SerializerException extends ODataLibraryException {
INCONSISTENT_PROPERTY_TYPE,
/** parameter: property name */
MISSING_PROPERTY,
/** parameter: Delta property name */
MISSING_DELTA_PROPERTY,
/** parameter: - */
MISSING_ID,
/** parameters: property name, property value */

View File

@ -22,6 +22,7 @@ import java.util.List;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
import org.apache.olingo.server.api.uri.queryoption.SkipOption;
@ -68,4 +69,9 @@ public interface UriInfoAll {
* @return Object containing information of the $top option
*/
TopOption getTopOption();
/**
* @return Object containing information of the $deltatoken option
*/
DeltaTokenOption getDeltaTokenOption();
}

View File

@ -21,6 +21,7 @@ package org.apache.olingo.server.api.uri;
import java.util.List;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@ -91,4 +92,9 @@ public interface UriInfoCrossjoin {
* @return Object containing information of the $top option
*/
TopOption getTopOption();
/**
* @return Object containing information of the $deltatoken option
*/
DeltaTokenOption getDeltaTokenOption();
}

View File

@ -23,6 +23,7 @@ import java.util.List;
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@ -69,7 +70,12 @@ public interface UriInfoResource {
* @return Object containing information of the $count option
*/
CountOption getCountOption();
/**
* @return Object containing information of the $deltatoken option
*/
DeltaTokenOption getDeltaTokenOption();
/**
* @return Object containing information of the $orderby option
*/

View File

@ -0,0 +1,28 @@
/*
* 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.server.api.uri.queryoption;
public interface DeltaTokenOption extends SystemQueryOption {
/**
* @return Value of $deltatoken
*/
String getValue();
}

View File

@ -83,7 +83,12 @@ public enum SystemQueryOptionKind {
* @see LevelsExpandOption
*/
LEVELS("$levels"),
/**
* @see deltaTokenOption
*/
DELTATOKEN("$deltatoken"),
/**
* @see ApplyOption
*/

View File

@ -47,6 +47,7 @@ import org.apache.olingo.server.api.uri.UriResourceRoot;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.api.uri.UriResourceValue;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@ -259,7 +260,10 @@ public class RequestURLHierarchyVisitor implements RequestURLVisitor {
if (info.getSkipTokenOption() != null) {
visit(info.getSkipTokenOption());
}
if (info.getDeltaTokenOption() != null) {
visit(info.getDeltaTokenOption());
}
}
@Override
@ -313,7 +317,11 @@ public class RequestURLHierarchyVisitor implements RequestURLVisitor {
@Override
public void visit(UriResourceRef info) {
}
@Override
public void visit(DeltaTokenOption option) {
}
@Override
public void visit(UriResourceRoot info) {
}

View File

@ -43,6 +43,7 @@ import org.apache.olingo.server.api.uri.UriResourceRoot;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.api.uri.UriResourceValue;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@ -96,7 +97,9 @@ public interface RequestURLVisitor {
void visit(TopOption option);
void visit(UriResourceCount option);
void visit(DeltaTokenOption option);
void visit(UriResourceRef info);
void visit(UriResourceRoot info);

View File

@ -39,6 +39,7 @@ import org.apache.olingo.server.api.etag.ETagHelper;
import org.apache.olingo.server.api.etag.ServiceMetadataETagSupport;
import org.apache.olingo.server.api.prefer.Preferences;
import org.apache.olingo.server.api.serializer.EdmAssistedSerializer;
import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
@ -53,6 +54,8 @@ import org.apache.olingo.server.core.prefer.PreferencesImpl;
import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl;
import org.apache.olingo.server.core.serializer.json.EdmAssistedJsonSerializer;
import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
import org.apache.olingo.server.core.serializer.json.JsonDeltaSerializer;
import org.apache.olingo.server.core.serializer.json.JsonDeltaSerializerWithNavigations;
import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
import org.apache.olingo.server.core.uri.UriHelperImpl;
@ -96,6 +99,33 @@ public class ODataImpl extends OData {
throw new SerializerException("Unsupported format: " + contentType.toContentTypeString(),
SerializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString());
}
@Override
public EdmDeltaSerializer createEdmDeltaSerializer(final ContentType contentType, final List<String> versions)
throws SerializerException {
if (contentType.isCompatible(ContentType.APPLICATION_JSON)) {
if(versions!=null && versions.size()>0){
return getMaxVersion(versions)>4 ? new JsonDeltaSerializerWithNavigations(contentType):
new JsonDeltaSerializer(contentType);
}
return new JsonDeltaSerializerWithNavigations(contentType);
}
throw new SerializerException("Unsupported format: " + contentType.toContentTypeString(),
SerializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString());
}
private float getMaxVersion(List<String> versions) {
Float versionValue [] = new Float [versions.size()];
int i=0;
Float max=new Float(0);
for(String version:versions){
Float ver = Float.valueOf(version);
versionValue[i++] = ver;
max = max > ver ? max : ver ;
}
return max;
}
@Override
public ODataHttpHandler createHandler(final ServiceMetadata serviceMetadata) {

View File

@ -105,7 +105,11 @@ public class DebugTabUri implements DebugTab {
} else if (uriInfo.getKind() == UriInfoKind.entityId) {
appendType(gen, "typeCast", uriInfo.asUriInfoEntityId().getEntityTypeCast());
}
if (uriInfo.getDeltaTokenOption() != null) {
gen.writeStringField("deltatoken", uriInfo.getDeltaTokenOption().getValue());
}
if (uriInfo.getFormatOption() != null) {
gen.writeStringField("format", uriInfo.getFormatOption().getFormat());
}

View File

@ -78,7 +78,8 @@ public class PreferencesImpl implements Preferences {
@Override
public boolean hasTrackChanges() {
return preferences.containsKey(PreferenceName.TRACK_CHANGES.getName());
return (preferences.containsKey(PreferenceName.TRACK_CHANGES.getName())
||preferences.containsKey(PreferenceName.TRACK_CHANGES_PREF.getName()));
}
@Override

View File

@ -0,0 +1,555 @@
/*
* 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.server.core.serializer.json;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.Delta;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.uri.UriHelper;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
import org.apache.olingo.server.core.uri.UriHelperImpl;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
public class JsonDeltaSerializer implements EdmDeltaSerializer {
private static final String LINK = "/$link";
private static final String DELETEDLINK = "/$deletedLink";
private static final String DELTA = "/$delta";
private static final String HASH = "#";
private static final String DELETEDENTITY = "/$deletedEntity";
private static final String ENTITY = "/$entity";
private static final String REASON = "Reason";
private static final String IO_EXCEPTION_TEXT = "An I/O exception occurred.";
private final boolean isIEEE754Compatible;
private final boolean isODataMetadataNone;
private final boolean isODataMetadataFull;
public JsonDeltaSerializer(final ContentType contentType) {
isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
}
@Override
public SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType referencedEntityType, Delta delta,
EntityCollectionSerializerOptions options) throws SerializerException {
OutputStream outputStream = null;
SerializerException cachedException = null;
try {
CircleStreamBuffer buffer = new CircleStreamBuffer();
outputStream = buffer.getOutputStream();
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
boolean pagination = false;
json.writeStartObject();
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
writeContextURL(contextURL, json);
if (options != null && options.getCount() != null && options.getCount().getValue()) {
writeInlineCount(delta.getCount(), json);
}
json.writeFieldName(Constants.VALUE);
writeEntitySet(metadata, referencedEntityType, delta, options, json);
pagination = writeNextLink(delta, json);
writeDeltaLink(delta, json, pagination);
json.close();
outputStream.close();
return SerializerResultImpl.with().content(buffer.getInputStream()).build();
} catch (final IOException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
}
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final Delta entitySet, final EntityCollectionSerializerOptions options,
final JsonGenerator json) throws IOException,
SerializerException {
json.writeStartArray();
for (final Entity entity : entitySet.getEntities()) {
writeAddedUpdatedEntity(metadata, entityType, entity, options.getExpand(),
options.getSelect(), options.getContextURL(), false, options.getContextURL()
.getEntitySetOrSingletonOrType(), json);
}
for (final DeletedEntity deletedEntity : entitySet.getDeletedEntities()) {
writeDeletedEntity(deletedEntity, options, json);
}
for (final DeltaLink addedLink : entitySet.getAddedLinks()) {
writeLink(addedLink, options, json, true);
}
for (final DeltaLink deletedLink : entitySet.getDeletedLinks()) {
writeLink(deletedLink, options, json, false);
}
json.writeEndArray();
}
private void writeLink(DeltaLink link, EntityCollectionSerializerOptions options,
JsonGenerator json, boolean isAdded) throws IOException, SerializerException {
try {
json.writeStartObject();
String entityId = options.getContextURL().getEntitySetOrSingletonOrType();
String operation = isAdded ? LINK : DELETEDLINK;
json.writeStringField(Constants.JSON_CONTEXT, HASH + entityId + operation);
if (link != null) {
if (link.getSource() != null) {
json.writeStringField(Constants.ATTR_SOURCE, link.getSource().toString());
} else {
throw new SerializerException("DeltaLink source is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Source");
}
if (link.getRelationship() != null) {
json.writeStringField(Constants.ATTR_RELATIONSHIP, link.getRelationship().toString());
} else {
throw new SerializerException("DeltaLink relationship is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Relationship");
}
if (link.getTarget() != null) {
json.writeStringField(Constants.ERROR_TARGET, link.getTarget().toString());
} else {
throw new SerializerException("DeltaLink target is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Target");
}
} else {
throw new SerializerException("DeltaLink is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Delta Link");
}
json.writeEndObject();
} catch (IOException e) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
}
}
private void writeDeletedEntity(DeletedEntity deletedEntity,
EntityCollectionSerializerOptions options, JsonGenerator json) throws IOException, SerializerException {
if (deletedEntity.getId() == null) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
}
if (deletedEntity.getReason() == null) {
throw new SerializerException("DeletedEntity reason is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, REASON);
}
json.writeStartObject();
json.writeStringField(Constants.JSON_CONTEXT, HASH + deletedEntity.getId().toASCIIString() + DELETEDENTITY);
json.writeStringField(Constants.JSON_ID, deletedEntity.getId().toASCIIString());
json.writeStringField(Constants.ELEM_REASON, deletedEntity.getReason().name());
json.writeEndObject();
}
public void writeAddedUpdatedEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final ExpandOption expand, final SelectOption select, final ContextURL url,
final boolean onlyReference, String name, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartObject();
if (entity.getId() != null && url != null) {
String entityId = entity.getId().toString();
name = url.getEntitySetOrSingletonOrType();
if (!entityId.contains(name)) {
String entityName = entityId.substring(0, entityId.indexOf("("));
if (!entityName.equals(name)) {
json.writeStringField(Constants.JSON_CONTEXT, HASH + entityName + ENTITY);
}
}
}
json.writeStringField(Constants.JSON_ID, getEntityId(entity, entityType, name));
writeProperties(metadata, entityType, entity.getProperties(), select, json);
json.writeEndObject();
}
private Property findProperty(final String propertyName, final List<Property> properties) {
for (final Property property : properties) {
if (propertyName.equals(property.getName())) {
return property;
}
}
return null;
}
protected void writeProperty(final ServiceMetadata metadata,
final EdmProperty edmProperty, final Property property,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
boolean isStreamProperty = isStreamProperty(edmProperty);
if (property != null) {
if (!isStreamProperty) {
json.writeFieldName(edmProperty.getName());
}
writePropertyValue(metadata, edmProperty, property, selectedPaths, json);
}
}
private boolean isStreamProperty(EdmProperty edmProperty) {
final EdmType type = edmProperty.getType();
return (edmProperty.isPrimitive() && type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream));
}
private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty,
final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
final EdmType type = edmProperty.getType();
try {
if (edmProperty.isPrimitive()
|| type.getKind() == EdmTypeKind.ENUM || type.getKind() == EdmTypeKind.DEFINITION) {
if (edmProperty.isCollection()) {
writePrimitiveCollection((EdmPrimitiveType) type, property,
edmProperty.isNullable(), edmProperty.getMaxLength(),
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json);
} else {
writePrimitive((EdmPrimitiveType) type, property,
edmProperty.isNullable(), edmProperty.getMaxLength(),
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json);
}
} else if (property.isComplex()) {
if (edmProperty.isCollection()) {
writeComplexCollection(metadata, (EdmComplexType) type, property, selectedPaths, json);
} else {
writeComplex(metadata, (EdmComplexType) type, property, selectedPaths, json);
}
} else {
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName());
}
} catch (final EdmPrimitiveTypeException e) {
throw new SerializerException("Wrong value for property!", e,
SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
edmProperty.getName(), property.getValue().toString());
}
}
protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, final EdmComplexType baseType,
final String derivedTypeName) throws SerializerException {
String fullQualifiedName = baseType.getFullQualifiedName().getFullQualifiedNameAsString();
if (derivedTypeName == null ||
fullQualifiedName.equals(derivedTypeName)) {
return baseType;
}
EdmComplexType derivedType = metadata.getEdm().getComplexType(new FullQualifiedName(derivedTypeName));
if (derivedType == null) {
throw new SerializerException("Complex Type not found",
SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
}
EdmComplexType type = derivedType.getBaseType();
while (type != null) {
if (type.getFullQualifiedName().equals(baseType.getFullQualifiedName())) {
return derivedType;
}
type = type.getBaseType();
}
throw new SerializerException("Wrong base type",
SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName,
baseType.getFullQualifiedName().getFullQualifiedNameAsString());
}
private void writeComplex(final ServiceMetadata metadata, final EdmComplexType type,
final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartObject();
String derivedName = property.getType();
final EdmComplexType resolvedType = resolveComplexType(metadata, (EdmComplexType) type, derivedName);
if (!isODataMetadataNone && !resolvedType.equals(type) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" + property.getType());
}
writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), selectedPaths,
json);
json.writeEndObject();
}
private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartArray();
for (Object value : property.asCollection()) {
switch (property.getValueType()) {
case COLLECTION_PRIMITIVE:
case COLLECTION_ENUM:
try {
writePrimitiveValue(property.getName(), type, value, isNullable,
maxLength, precision, scale, isUnicode, json);
} catch (EdmPrimitiveTypeException e) {
throw new SerializerException("Wrong value for property!", e,
SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
property.getName(), property.getValue().toString());
}
break;
case COLLECTION_GEOSPATIAL:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
default:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
}
}
json.writeEndArray();
}
private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type,
final Property property,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartArray();
for (Object value : property.asCollection()) {
switch (property.getValueType()) {
case COLLECTION_COMPLEX:
json.writeStartObject();
if (isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" +
type.getFullQualifiedName().getFullQualifiedNameAsString());
}
writeComplexValue(metadata, type, ((ComplexValue) value).getValue(), selectedPaths, json);
json.writeEndObject();
break;
default:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
}
}
json.writeEndArray();
}
private void writePrimitive(final EdmPrimitiveType type, final Property property,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json)
throws EdmPrimitiveTypeException, IOException, SerializerException {
if (property.isPrimitive()) {
writePrimitiveValue(property.getName(), type, property.asPrimitive(),
isNullable, maxLength, precision, scale, isUnicode, json);
} else if (property.isGeospatial()) {
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
} else if (property.isEnum()) {
writePrimitiveValue(property.getName(), type, property.asEnum(),
isNullable, maxLength, precision, scale, isUnicode, json);
} else {
throw new SerializerException("Inconsistent property type!",
SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, property.getName());
}
}
protected void writePrimitiveValue(final String name, final EdmPrimitiveType type, final Object primitiveValue,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json) throws EdmPrimitiveTypeException, IOException {
final String value = type.valueToString(primitiveValue,
isNullable, maxLength, precision, scale, isUnicode);
if (value == null) {
json.writeNull();
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
json.writeBoolean(Boolean.parseBoolean(value));
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)
|| (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64))
&& !isIEEE754Compatible) {
json.writeNumber(value);
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) {
if (primitiveValue instanceof Link) {
Link stream = (Link) primitiveValue;
if (!isODataMetadataNone) {
if (stream.getMediaETag() != null) {
json.writeStringField(name + Constants.JSON_MEDIA_ETAG, stream.getMediaETag());
}
if (stream.getType() != null) {
json.writeStringField(name + Constants.JSON_MEDIA_CONTENT_TYPE, stream.getType());
}
}
if (isODataMetadataFull) {
if (stream.getRel() != null && stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {
json.writeStringField(name + Constants.JSON_MEDIA_READ_LINK, stream.getHref());
}
if (stream.getRel() == null || stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {
json.writeStringField(name + Constants.JSON_MEDIA_EDIT_LINK, stream.getHref());
}
}
}
} else {
json.writeString(value);
}
}
protected void writeComplexValue(final ServiceMetadata metadata,
final EdmComplexType type, final List<Property> properties,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
for (final String propertyName : type.getPropertyNames()) {
final Property property = findProperty(propertyName, properties);
if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), property,
selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
json);
}
}
}
protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type,
final List<Property> properties,
final SelectOption select, final JsonGenerator json)
throws IOException, SerializerException {
final boolean all = ExpandSelectHelper.isAll(select);
final Set<String> selected = all ? new HashSet<String>() : ExpandSelectHelper.getSelectedPropertyNames(select
.getSelectItems());
for (final String propertyName : type.getPropertyNames()) {
if (all || selected.contains(propertyName)) {
final EdmProperty edmProperty = type.getStructuralProperty(propertyName);
final Property property = findProperty(propertyName, properties);
final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null : ExpandSelectHelper
.getSelectedPaths(select.getSelectItems(), propertyName);
writeProperty(metadata, edmProperty, property, selectedPaths, json);
}
}
}
/**
* Get the ascii representation of the entity id
* or thrown an {@link SerializerException} if id is <code>null</code>.
*
* @param entity the entity
* @return ascii representation of the entity id
*/
private String getEntityId(Entity entity, EdmEntityType entityType, String name) throws SerializerException {
try {
if (entity != null) {
if (entity.getId() == null) {
if (entityType == null || entityType.getKeyPredicateNames() == null
|| name == null) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
} else {
final UriHelper uriHelper = new UriHelperImpl();
entity.setId(URI.create(name + '(' + uriHelper.buildKeyPredicate(entityType, entity) + ')'));
return entity.getId().toASCIIString();
}
} else {
return entity.getId().toASCIIString();
}
}
return null;
} catch (Exception e) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
}
}
void writeInlineCount(final Integer count, final JsonGenerator json)
throws IOException {
if (count != null) {
String countValue = isIEEE754Compatible ? String.valueOf(count) : String.valueOf(count);
json.writeStringField(Constants.JSON_COUNT, countValue);
}
}
ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
if (isODataMetadataNone) {
return null;
} else if (contextURL == null) {
throw new SerializerException("ContextURL null!", SerializerException.MessageKeys.NO_CONTEXT_URL);
}
return contextURL;
}
void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException {
if (!isODataMetadataNone && contextURL != null) {
json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString() + DELTA);
}
}
boolean writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json)
throws IOException {
if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
return true;
} else {
return false;
}
}
void writeDeltaLink(final AbstractEntityCollection entitySet, final JsonGenerator json, boolean pagination)
throws IOException {
if (entitySet.getDeltaLink() != null && !pagination) {
json.writeStringField(Constants.JSON_DELTA_LINK, entitySet.getDeltaLink().toASCIIString());
}
}
protected void closeCircleStreamBufferOutput(final OutputStream outputStream,
final SerializerException cachedException)
throws SerializerException {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
if (cachedException != null) {
throw cachedException;
} else {
throw new SerializerException(IO_EXCEPTION_TEXT, e,
SerializerException.MessageKeys.IO_EXCEPTION);
}
}
}
}
}

View File

@ -0,0 +1,655 @@
/*
* 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.server.core.serializer.json;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.Delta;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.uri.UriHelper;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
import org.apache.olingo.server.core.uri.UriHelperImpl;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
public class JsonDeltaSerializerWithNavigations implements EdmDeltaSerializer {
private static final String IO_EXCEPTION_TEXT = "An I/O exception occurred.";
private final boolean isIEEE754Compatible;
private final boolean isODataMetadataNone;
private final boolean isODataMetadataFull;
public JsonDeltaSerializerWithNavigations(final ContentType contentType) {
isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
}
@Override
public SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType referencedEntityType, Delta delta,
EntityCollectionSerializerOptions options) throws SerializerException {
OutputStream outputStream = null;
SerializerException cachedException = null;
boolean pagination = false;
try {
CircleStreamBuffer buffer = new CircleStreamBuffer();
outputStream = buffer.getOutputStream();
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
json.writeStartObject();
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
writeContextURL(contextURL, json);
if (options != null && options.getCount() != null && options.getCount().getValue()) {
writeInlineCount(delta.getCount(), json);
}
json.writeFieldName(Constants.VALUE);
writeEntitySet(metadata, referencedEntityType, delta, options, json);
pagination = writeNextLink(delta, json);
writeDeltaLink(delta, json, pagination);
json.close();
outputStream.close();
return SerializerResultImpl.with().content(buffer.getInputStream()).build();
} catch (final IOException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
}
protected void closeCircleStreamBufferOutput(final OutputStream outputStream,
final SerializerException cachedException)
throws SerializerException {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
if (cachedException != null) {
throw cachedException;
} else {
throw new SerializerException(IO_EXCEPTION_TEXT, e,
SerializerException.MessageKeys.IO_EXCEPTION);
}
}
}
}
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final Delta entitySet, final EntityCollectionSerializerOptions options,
final JsonGenerator json) throws IOException,
SerializerException {
json.writeStartArray();
for (final Entity entity : entitySet.getEntities()) {
writeAddedUpdatedEntity(metadata, entityType, entity, options.getExpand(), options.getSelect(),
options.getContextURL(), false, options.getContextURL().getEntitySetOrSingletonOrType(), json);
}
for (final DeletedEntity deletedEntity : entitySet.getDeletedEntities()) {
writeDeletedEntity(deletedEntity, json);
}
for (final DeltaLink addedLink : entitySet.getAddedLinks()) {
writeLink(addedLink, options, json, true);
}
for (final DeltaLink deletedLink : entitySet.getDeletedLinks()) {
writeLink(deletedLink, options, json, false);
}
json.writeEndArray();
}
private void writeLink(DeltaLink link, EntityCollectionSerializerOptions options,
JsonGenerator json, boolean isAdded) throws IOException, SerializerException {
try {
json.writeStartObject();
String entityId = options.getContextURL().getEntitySetOrSingletonOrType();// throw error if not set id
String operation = isAdded ? Constants.LINK : Constants.DELETEDLINK;
json.writeStringField(Constants.AT + Constants.CONTEXT, Constants.HASH + entityId + operation);
if (link != null) {
if (link.getSource() != null) {
json.writeStringField(Constants.ATTR_SOURCE, link.getSource().toString());
} else {
throw new SerializerException("DeltaLink source is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Source");
}
if (link.getRelationship() != null) {
json.writeStringField(Constants.ATTR_RELATIONSHIP, link.getRelationship().toString());
} else {
throw new SerializerException("DeltaLink relationship is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Relationship");
}
if (link.getTarget() != null) {
json.writeStringField(Constants.ERROR_TARGET, link.getTarget().toString());
} else {
throw new SerializerException("DeltaLink target is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Target");
}
} else {
throw new SerializerException("DeltaLink is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Delta Link");
}
json.writeEndObject();
} catch (IOException e) {
throw new SerializerException("Entity id is null.",
SerializerException.MessageKeys.MISSING_ID);
}
}
private void writeDeletedEntity(Entity deletedEntity,
JsonGenerator json) throws IOException, SerializerException {
if (deletedEntity.getId() == null) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
}
json.writeStartObject();
if (isODataMetadataFull) {
json.writeStringField(Constants.AT + Constants.CONTEXT, Constants.HASH + deletedEntity.getId().toASCIIString()
+ Constants.DELETEDENTITY);
}
if (((DeletedEntity) deletedEntity).getReason() == null) {
throw new SerializerException("DeletedEntity reason is null.",
SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, Constants.REASON);
}
json.writeFieldName(Constants.AT + Constants.REMOVED);
json.writeStartObject();
json.writeStringField(Constants.ELEM_REASON,
((DeletedEntity) deletedEntity).getReason().name());
List<Annotation> annotations = deletedEntity.getAnnotations();
if (annotations != null && annotations.size() > 0) {
for (Annotation annotation : annotations) {
json.writeStringField(Constants.AT + annotation.getTerm(), annotation.getValue().toString());
}
}
json.writeEndObject();
List<Property> properties = deletedEntity.getProperties();
if (properties != null && properties.size() > 0) {
for (Property property : properties) {
json.writeStringField(property.getName(), property.getValue().toString());
}
}
json.writeStringField(Constants.AT + Constants.ATOM_ATTR_ID, deletedEntity.getId().toASCIIString());
json.writeEndObject();
}
public void writeAddedUpdatedEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final ExpandOption expand, final SelectOption select, final ContextURL url,
final boolean onlyReference, String name, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartObject();
if (entity.getId() != null && url != null) {
name = url.getEntitySetOrSingletonOrType();
String entityId = entity.getId().toString();
if (!entityId.contains(name)) {
String entityName = entityId.substring(0, entityId.indexOf("("));
if (!entityName.equals(name)) {
json.writeStringField(Constants.AT + Constants.CONTEXT, Constants.HASH + entityName
+ Constants.ENTITY);
}
}
}
json.writeStringField(Constants.AT + Constants.ATOM_ATTR_ID, getEntityId(entity, entityType, name));
writeProperties(metadata, entityType, entity.getProperties(), select, json);
writeNavigationProperties(metadata, entityType, entity, expand, name, json);
json.writeEndObject();
}
private Property findProperty(final String propertyName, final List<Property> properties) {
for (final Property property : properties) {
if (propertyName.equals(property.getName())) {
return property;
}
}
return null;
}
protected void writeProperty(final ServiceMetadata metadata,
final EdmProperty edmProperty, final Property property,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
boolean isStreamProperty = isStreamProperty(edmProperty);
if (property != null) {
if (!isStreamProperty) {
json.writeFieldName(edmProperty.getName());
}
writePropertyValue(metadata, edmProperty, property, selectedPaths, json);
}
}
private boolean isStreamProperty(EdmProperty edmProperty) {
final EdmType type = edmProperty.getType();
return (edmProperty.isPrimitive() && type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream));
}
private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty,
final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
final EdmType type = edmProperty.getType();
try {
if (edmProperty.isPrimitive()
|| type.getKind() == EdmTypeKind.ENUM || type.getKind() == EdmTypeKind.DEFINITION) {
if (edmProperty.isCollection()) {
writePrimitiveCollection((EdmPrimitiveType) type, property,
edmProperty.isNullable(), edmProperty.getMaxLength(),
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json);
} else {
writePrimitive((EdmPrimitiveType) type, property,
edmProperty.isNullable(), edmProperty.getMaxLength(),
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json);
}
} else if (property.isComplex()) {
if (edmProperty.isCollection()) {
writeComplexCollection(metadata, (EdmComplexType) type, property, selectedPaths, json);
} else {
writeComplex(metadata, (EdmComplexType) type, property, selectedPaths, json);
}
} else {
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName());
}
} catch (final EdmPrimitiveTypeException e) {
throw new SerializerException("Wrong value for property!", e,
SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
edmProperty.getName(), property.getValue().toString());
}
}
protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, final EdmComplexType baseType,
final String derivedTypeName) throws SerializerException {
String fullQualifiedName = baseType.getFullQualifiedName().getFullQualifiedNameAsString();
if (derivedTypeName == null ||
fullQualifiedName.equals(derivedTypeName)) {
return baseType;
}
EdmComplexType derivedType = metadata.getEdm().getComplexType(new FullQualifiedName(derivedTypeName));
if (derivedType == null) {
throw new SerializerException("Complex Type not found",
SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
}
EdmComplexType type = derivedType.getBaseType();
while (type != null) {
if (type.getFullQualifiedName().equals(baseType.getFullQualifiedName())) {
return derivedType;
}
type = type.getBaseType();
}
throw new SerializerException("Wrong base type",
SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName,
baseType.getFullQualifiedName().getFullQualifiedNameAsString());
}
private void writeComplex(final ServiceMetadata metadata, final EdmComplexType type,
final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartObject();
String derivedName = property.getType();
final EdmComplexType resolvedType = resolveComplexType(metadata, (EdmComplexType) type, derivedName);
if (!isODataMetadataNone && !resolvedType.equals(type) || isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" + property.getType());
}
writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), selectedPaths,
json);
json.writeEndObject();
}
private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartArray();
for (Object value : property.asCollection()) {
switch (property.getValueType()) {
case COLLECTION_PRIMITIVE:
case COLLECTION_ENUM:
try {
writePrimitiveValue(property.getName(), type, value, isNullable,
maxLength, precision, scale, isUnicode, json);
} catch (EdmPrimitiveTypeException e) {
throw new SerializerException("Wrong value for property!", e,
SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
property.getName(), property.getValue().toString());
}
break;
case COLLECTION_GEOSPATIAL:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
default:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
}
}
json.writeEndArray();
}
private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type,
final Property property,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartArray();
for (Object value : property.asCollection()) {
switch (property.getValueType()) {
case COLLECTION_COMPLEX:
json.writeStartObject();
if (isODataMetadataFull) {
json.writeStringField(Constants.JSON_TYPE, "#" +
type.getFullQualifiedName().getFullQualifiedNameAsString());
}
writeComplexValue(metadata, type, ((ComplexValue) value).getValue(), selectedPaths, json);
json.writeEndObject();
break;
default:
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
}
}
json.writeEndArray();
}
private void writePrimitive(final EdmPrimitiveType type, final Property property,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json)
throws EdmPrimitiveTypeException, IOException, SerializerException {
if (property.isPrimitive()) {
writePrimitiveValue(property.getName(), type, property.asPrimitive(),
isNullable, maxLength, precision, scale, isUnicode, json);
} else if (property.isGeospatial()) {
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
} else if (property.isEnum()) {
writePrimitiveValue(property.getName(), type, property.asEnum(),
isNullable, maxLength, precision, scale, isUnicode, json);
} else {
throw new SerializerException("Inconsistent property type!",
SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, property.getName());
}
}
protected void writePrimitiveValue(final String name, final EdmPrimitiveType type, final Object primitiveValue,
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
final Boolean isUnicode, final JsonGenerator json) throws EdmPrimitiveTypeException, IOException {
final String value = type.valueToString(primitiveValue,
isNullable, maxLength, precision, scale, isUnicode);
if (value == null) {
json.writeNull();
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
json.writeBoolean(Boolean.parseBoolean(value));
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)
|| (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)
|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64))
&& !isIEEE754Compatible) {
json.writeNumber(value);
} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) {
if (primitiveValue instanceof Link) {
Link stream = (Link) primitiveValue;
if (!isODataMetadataNone) {
if (stream.getMediaETag() != null) {
json.writeStringField(name + Constants.JSON_MEDIA_ETAG, stream.getMediaETag());
}
if (stream.getType() != null) {
json.writeStringField(name + Constants.JSON_MEDIA_CONTENT_TYPE, stream.getType());
}
}
if (isODataMetadataFull) {
if (stream.getRel() != null && stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {
json.writeStringField(name + Constants.JSON_MEDIA_READ_LINK, stream.getHref());
}
if (stream.getRel() == null || stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {
json.writeStringField(name + Constants.JSON_MEDIA_EDIT_LINK, stream.getHref());
}
}
}
} else {
json.writeString(value);
}
}
protected void writeComplexValue(final ServiceMetadata metadata,
final EdmComplexType type, final List<Property> properties,
final Set<List<String>> selectedPaths, final JsonGenerator json)
throws IOException, SerializerException {
for (final String propertyName : type.getPropertyNames()) {
final Property property = findProperty(propertyName, properties);
if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), property,
selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
json);
}
}
}
protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type,
final List<Property> properties,
final SelectOption select, final JsonGenerator json)
throws IOException, SerializerException {
final boolean all = ExpandSelectHelper.isAll(select);
final Set<String> selected = all ? new HashSet<String>() : ExpandSelectHelper.getSelectedPropertyNames(select
.getSelectItems());
for (final String propertyName : type.getPropertyNames()) {
if ((all || selected.contains(propertyName)) && properties.size() > 0) {
final EdmProperty edmProperty = type.getStructuralProperty(propertyName);
final Property property = findProperty(propertyName, properties);
final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null : ExpandSelectHelper
.getSelectedPaths(select.getSelectItems(), propertyName);
writeProperty(metadata, edmProperty, property, selectedPaths, json);
}
}
}
protected void writeNavigationProperties(final ServiceMetadata metadata,
final EdmStructuredType type, final Linked linked, final ExpandOption expand,
final String name, final JsonGenerator json) throws SerializerException, IOException {
if (ExpandSelectHelper.hasExpand(expand)) {
final boolean expandAll = ExpandSelectHelper.getExpandAll(expand) != null;
final Set<String> expanded = expandAll ? new HashSet<String>() : ExpandSelectHelper.getExpandedPropertyNames(
expand.getExpandItems());
for (final String propertyName : type.getNavigationPropertyNames()) {
if (expandAll || expanded.contains(propertyName)) {
final EdmNavigationProperty property = type.getNavigationProperty(propertyName);
final Link navigationLink = linked.getNavigationLink(property.getName());
final ExpandItem innerOptions = expandAll ? null : ExpandSelectHelper.getExpandItem(expand.getExpandItems(),
propertyName);
if (innerOptions != null && innerOptions.getLevelsOption() != null) {
throw new SerializerException("Expand option $levels is not supported.",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
}
if (navigationLink != null) {
writeExpandedNavigationProperty(metadata, property, navigationLink,
innerOptions == null ? null : innerOptions.getExpandOption(),
innerOptions == null ? null : innerOptions.getSelectOption(),
innerOptions == null ? null : innerOptions.getCountOption(),
innerOptions == null ? false : innerOptions.hasCountPath(),
innerOptions == null ? false : innerOptions.isRef(),
name, json);
}
}
}
}
}
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select,
final boolean onlyReference, String name, final JsonGenerator json) throws IOException,
SerializerException {
json.writeStartArray();
for (final Entity entity : entitySet) {
if (onlyReference) {
json.writeStartObject();
json.writeStringField(Constants.JSON_ID, getEntityId(entity, entityType, null));
json.writeEndObject();
} else {
if (entity instanceof DeletedEntity) {
writeDeletedEntity(entity, json);
} else {
writeAddedUpdatedEntity(metadata, entityType, entity, expand, select, null, false, name, json);
}
}
}
json.writeEndArray();
}
protected void writeExpandedNavigationProperty(
final ServiceMetadata metadata, final EdmNavigationProperty property,
final Link navigationLink, final ExpandOption innerExpand,
final SelectOption innerSelect, final CountOption innerCount,
final boolean writeOnlyCount, final boolean writeOnlyRef, final String name,
final JsonGenerator json) throws IOException, SerializerException {
if (property.isCollection()) {
if (navigationLink != null && navigationLink.getInlineEntitySet() != null) {
json.writeFieldName(property.getName() + Constants.AT + Constants.DELTAVALUE);
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
innerSelect, writeOnlyRef, name, json);
}
} else {
json.writeFieldName(property.getName()+ Constants.AT + Constants.DELTAVALUE);
if (navigationLink != null && navigationLink.getInlineEntity() != null) {
if (navigationLink.getInlineEntity() instanceof DeletedEntity) {
writeDeletedEntity(navigationLink.getInlineEntity(), json);
} else {
writeAddedUpdatedEntity(metadata, property.getType(), navigationLink.getInlineEntity(),
innerExpand, innerSelect, null, writeOnlyRef, name, json);
}
}
}
}
/**
* Get the ascii representation of the entity id
* or thrown an {@link SerializerException} if id is <code>null</code>.
*
* @param entity the entity
* @return ascii representation of the entity id
*/
private String getEntityId(Entity entity, EdmEntityType entityType, String name) throws SerializerException {
try {
if (entity != null) {
if (entity.getId() == null) {
if (entityType == null || entityType.getKeyPredicateNames() == null
|| name == null) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
} else {
final UriHelper uriHelper = new UriHelperImpl();
entity.setId(URI.create(name + '(' + uriHelper.buildKeyPredicate(entityType, entity) + ')'));
return entity.getId().toASCIIString();
}
} else {
return entity.getId().toASCIIString();
}
}
return null;
} catch (Exception e) {
throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID);
}
}
void writeInlineCount(final Integer count, final JsonGenerator json)
throws IOException {
if (count != null) {
String countValue = isIEEE754Compatible ? String.valueOf(count) : String.valueOf(count);
json.writeStringField(Constants.AT + Constants.ATOM_ELEM_COUNT, countValue);
}
}
ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
if (isODataMetadataNone) {
return null;
} else if (contextURL == null) {
throw new SerializerException("ContextURL null!", SerializerException.MessageKeys.NO_CONTEXT_URL);
}
return contextURL;
}
void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException {
if (!isODataMetadataNone && contextURL != null) {
String context = Constants.AT + Constants.CONTEXT;
json.writeStringField(context, ContextURLBuilder.create(contextURL).toASCIIString() + Constants.DELTA);
}
}
boolean writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json)
throws IOException {
if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
return true;
} else {
return false;
}
}
void writeDeltaLink(final AbstractEntityCollection entitySet, final JsonGenerator json,
final boolean pagination)
throws IOException {
if (entitySet.getDeltaLink() != null && !pagination) {
json.writeStringField(Constants.DELTALINK, entitySet.getDeltaLink().toASCIIString());
}
}
}

View File

@ -174,6 +174,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
final EntityCollectionSerializerOptions options) throws SerializerException {
OutputStream outputStream = null;
SerializerException cachedException = null;
boolean pagination = false;
try {
CircleStreamBuffer buffer = new CircleStreamBuffer();
outputStream = buffer.getOutputStream();
@ -197,7 +198,8 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
writeEntitySet(metadata, entityType, entitySet,
options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), null, name, json);
}
writeNextLink(entitySet, json);
writeNextLink(entitySet, json, pagination);
writeDeltaLink(entitySet, json, pagination);
json.close();
outputStream.close();
@ -224,6 +226,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
throws SerializerException {
SerializerException cachedException;
boolean pagination = false;
try {
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
json.writeStartObject();
@ -245,7 +248,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), null, name, json);
}
// next link support for streaming results
writeNextLink(entitySet, json);
writeNextLink(entitySet, json, pagination);
json.close();
} catch (final IOException e) {
@ -1165,6 +1168,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
throws SerializerException {
OutputStream outputStream = null;
SerializerException cachedException = null;
boolean pagination = false ;
try {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
@ -1187,7 +1191,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
json.writeEndArray();
writeNextLink(entityCollection, json);
writeNextLink(entityCollection, json, pagination);
json.writeEndObject();
@ -1231,9 +1235,20 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json) throws IOException {
void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json, boolean pagination)
throws IOException {
if (entitySet.getNext() != null) {
pagination = true;
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
}else{
pagination = false;
}
}
void writeDeltaLink(final AbstractEntityCollection entitySet, final JsonGenerator json, boolean pagination)
throws IOException {
if (entitySet.getDeltaLink() != null && !pagination) {
json.writeStringField(Constants.JSON_DELTA_LINK, entitySet.getDeltaLink().toASCIIString());
}
}
}

View File

@ -40,6 +40,7 @@ import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
@ -189,6 +190,7 @@ public class UriInfoImpl implements UriInfo {
case SELECT:
case SKIP:
case SKIPTOKEN:
case DELTATOKEN:
case TOP:
case LEVELS:
case APPLY:
@ -310,4 +312,9 @@ public class UriInfoImpl implements UriInfo {
public String getFragment() {
return fragment;
}
@Override
public DeltaTokenOption getDeltaTokenOption() {
return (DeltaTokenOption) systemQueryOptions.get(SystemQueryOptionKind.DELTATOKEN);
}
}

View File

@ -58,6 +58,7 @@ import org.apache.olingo.server.core.uri.parser.search.SearchParser;
import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ApplyOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.DeltaTokenOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
@ -332,6 +333,14 @@ public class Parser {
}
systemOption = new SkipTokenOptionImpl().setValue(optionValue);
break;
case DELTATOKEN:
if (optionValue.isEmpty()) {
throw new UriParserSyntaxException("Illegal value of $deltatoken option!",
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
optionName, optionValue);
}
systemOption = new DeltaTokenOptionImpl().setValue(optionValue);
break;
case TOP:
systemOption = new TopOptionImpl()
.setValue(ParserHelper.parseNonNegativeInteger(optionName, optionValue, true));

View File

@ -0,0 +1,39 @@
/*
* 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.server.core.uri.queryoption;
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
public class DeltaTokenOptionImpl extends SystemQueryOptionImpl implements DeltaTokenOption {
public DeltaTokenOptionImpl() {
setKind(SystemQueryOptionKind.DELTATOKEN);
}
@Override
public String getValue() {
return getText();
}
public DeltaTokenOptionImpl setValue(final String deltaTokenValue) {
setText(deltaTokenValue);
return this;
}
}

View File

@ -45,27 +45,27 @@ public class UriValidator {
//CHECKSTYLE:OFF (Maven checkstyle)
private static final boolean[][] decisionMatrix =
{
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-TOP 11-APPLY */
/* all 0 */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* batch 1 */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* crossjoin 2 */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entityId 3 */ { false, true , true , true , false, false, false, true , false, false, false, false },
/* metadata 4 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* service 5 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* entitySet 6 */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entitySetCount 7 */ { true , false, false, false, false, false, true , false, false, false, false, true },
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false, false },
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* references 10 */ { true , true , false, false, true , true , true , false, true , true , true , false },
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false, false },
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true , true },
/* propertyComplexCollectionCount 14 */ { true , false, false, false, false, false, false, false, false, false, false, true },
/* propertyPrimitive 15 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveCollection 16 */ { true , true , false, false, true , true , false, false, true , true , true , false },
/* propertyPrimitiveCollectionCount 17 */ { true , false, false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveValue 18 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* none 19 */ { false, true , false, false, false, false, false, false, false, false, false, false }
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-TOP 11-APPLY 12-DELTATOKEN */
/* all 0 */ { true , true , true , false, true , true , true , true , true , true , true , true, true },
/* batch 1 */ { false, false, false, false, false, false, false, false, false, false, false, false, false },
/* crossjoin 2 */ { true , true , true , false, true , true , true , true , true , true , true , true, true },
/* entityId 3 */ { false, true , true , true , false, false, false, true , false, false, false, false, false },
/* metadata 4 */ { false, true , false, false, false, false, false, false, false, false, false, false, false },
/* service 5 */ { false, true , false, false, false, false, false, false, false, false, false, false, false },
/* entitySet 6 */ { true , true , true , false, true , true , true , true , true , true , true , true, true },
/* entitySetCount 7 */ { true , false, false, false, false, false, true , false, false, false, false, true, true },
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false, false, false },
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false, false, false },
/* references 10 */ { true , true , false, false, true , true , true , false, true , true , true , false, true },
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false, false, false },
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false, false, false },
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true , true , true },
/* propertyComplexCollectionCount 14 */ { true , false, false, false, false, false, false, false, false, false, false, true , false },
/* propertyPrimitive 15 */ { false, true , false, false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveCollection 16 */ { true , true , false, false, true , true , false, false, true , true , true , false, true },
/* propertyPrimitiveCollectionCount 17 */ { true , false, false, false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveValue 18 */ { false, true , false, false, false, false, false, false, false, false, false, false, false },
/* none 19 */ { false, true , false, false, false, false, false, false, false, false, false, false, false }
};
//CHECKSTYLE:ON
//@formatter:on
@ -119,6 +119,7 @@ public class UriValidator {
temp.put(SystemQueryOptionKind.SKIPTOKEN, 9);
temp.put(SystemQueryOptionKind.TOP, 10);
temp.put(SystemQueryOptionKind.APPLY, 11);
temp.put(SystemQueryOptionKind.DELTATOKEN, 12);
OPTION_INDEX = Collections.unmodifiableMap(temp);
}

View File

@ -111,6 +111,7 @@ SerializerException.IO_EXCEPTION=An I/O exception occurred.
SerializerException.NULL_INPUT=The input 'null' is not allowed here.
SerializerException.NO_CONTEXT_URL=No context URL has been provided.
SerializerException.UNSUPPORTED_PROPERTY_TYPE=The type of the property '%1$s' is not yet supported.
SerializerException.MISSING_DELTA_PROPERTY=The delta property '%1$s' is missing.
SerializerException.INCONSISTENT_PROPERTY_TYPE=An inconsistency has been detected in the type definition of property '%1$s'.
SerializerException.MISSING_PROPERTY=The non-nullable property '%1$s' is missing.
SerializerException.MISSING_ID=The entity id value is missing.

View File

@ -36,6 +36,7 @@ import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ApplyOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.DeltaTokenOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
@ -121,7 +122,8 @@ public class UriInfoImplTest {
final QueryOption skipToken = new SkipTokenOptionImpl().setName("");
final QueryOption top = new TopOptionImpl().setName("");
final QueryOption levels = new LevelsOptionImpl().setName("");
final QueryOption deltaToken = new DeltaTokenOptionImpl().setName("");
final QueryOption customOption0 = new CustomQueryOptionImpl().setName("0").setText("A");
final QueryOption customOption1 = new CustomQueryOptionImpl().setName("1").setText("B");
@ -146,9 +148,10 @@ public class UriInfoImplTest {
.setQueryOption(customOption1)
.setQueryOption(levels)
.setQueryOption(initialQueryOption)
.setQueryOption(alias);
.setQueryOption(alias)
.setQueryOption(deltaToken);
assertEquals(13, uriInfo.getSystemQueryOptions().size());
assertEquals(14, uriInfo.getSystemQueryOptions().size());
assertEquals(apply, uriInfo.getApplyOption());
assertEquals(expand, uriInfo.getExpandOption());
assertEquals(filter, uriInfo.getFilterOption());
@ -161,6 +164,7 @@ public class UriInfoImplTest {
assertEquals(skip, uriInfo.getSkipOption());
assertEquals(skipToken, uriInfo.getSkipTokenOption());
assertEquals(top, uriInfo.getTopOption());
assertEquals(deltaToken, uriInfo.getDeltaTokenOption());
assertArrayEquals(new QueryOption[] { alias }, uriInfo.getAliases().toArray());
assertEquals("C", uriInfo.getValueForAlias("alias"));

View File

@ -94,7 +94,8 @@ public class DataCreator {
data.put("ESCompCollDerived", createESCompCollDerived(edm, odata));
data.put("ESTwoPrimDerived", createESTwoPrimDerived(edm, odata));
data.put("ESAllPrimDerived", createESAllPrimDerived(edm, odata));
data.put("ESDelta", createESDelta(edm, odata));
linkSINav(data);
linkESTwoPrim(data);
linkESAllPrim(data);
@ -102,6 +103,34 @@ public class DataCreator {
linkESKeyNav(data);
linkESTwoKeyNav(data);
linkESPeople(data);
linkESDelta(data);
}
private EntityCollection createESDelta(final Edm edm, final OData odata) {
EntityCollection entityCollection = new EntityCollection();
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
.addProperty(createPrimitive("PropertyString", "Number:" + Short.MAX_VALUE)));
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", Short.MIN_VALUE))
.addProperty(createPrimitive("PropertyString", "Number:" + Short.MIN_VALUE)));
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", 0))
.addProperty(createPrimitive("PropertyString", "Number:" + 0)));
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", 100))
.addProperty(createPrimitive("PropertyString", "Number:" + 100)));
entityCollection.getEntities().add(new Entity()
.addProperty(createPrimitive("PropertyInt16", -1))
.addProperty(createPrimitive("PropertyString", "Number:" + -1)));
setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETDelta));
createEntityId(edm, odata, "ESDelta", entityCollection);
createOperations("ESDelta", entityCollection, EntityTypeProvider.nameETDelta);
return entityCollection;
}
private EntityCollection createSINav(Edm edm, OData odata) {
@ -1511,6 +1540,16 @@ public class DataCreator {
+ " <g stroke=\"darkmagenta\" stroke-width=\"16\" fill=\"" + color + "\">\n"
+ " <circle cx=\"50\" cy=\"50\" r=\"42\"/>\n" + " </g>\n" + "</svg>\n").getBytes(Charset.forName("UTF-8"));
}
private void linkESDelta(final Map<String, EntityCollection> data) {
final EntityCollection entityCollection = data.get("ESDelta");
final List<Entity> targetEntities = data.get("ESAllPrim").getEntities();
setLinks(entityCollection.getEntities().get(0), "NavPropertyETAllPrimMany", targetEntities.get(1),
targetEntities.get(2));
setLink(entityCollection.getEntities().get(3), "NavPropertyETAllPrimOne", targetEntities.get(0));
}
private void linkESPeople(final Map<String, EntityCollection> data) {
final EntityCollection entityCollection = data.get("ESPeople");

View File

@ -32,6 +32,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.DeletedEntity.Reason;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
@ -563,7 +566,54 @@ public class DataProvider {
entity.setMediaContentType(type);
entity.setMediaETag("W/\"" + UUID.randomUUID() + "\"");
}
public List<DeletedEntity> readDeletedEntities(final EdmEntitySet edmEntitySet) throws DataProviderException {
EntityCollection entityCollection = data.get(edmEntitySet.getName());
List<DeletedEntity> listOfDeletedEntities = new ArrayList<DeletedEntity>();
DeletedEntity entitySetDeleted = new DeletedEntity();
if (entityCollection.getEntities().size() > 1) {
entitySetDeleted.setId(entityCollection.getEntities().get(0).getId());
entitySetDeleted.setReason(Reason.changed);
listOfDeletedEntities.add(entitySetDeleted);
entitySetDeleted = new DeletedEntity();
entitySetDeleted.setId(entityCollection.getEntities().get(1).getId());
entitySetDeleted.setReason(Reason.deleted);
}
listOfDeletedEntities.add(entitySetDeleted);
return listOfDeletedEntities;
}
public List<DeltaLink> readAddedLinks(final EdmEntitySet edmEntitySet) throws DataProviderException {
EntityCollection entityCollection = data.get(edmEntitySet.getName());
List<DeltaLink> listOfAddedLinks = new ArrayList<DeltaLink>();
DeltaLink link = new DeltaLink();
if (entityCollection.getEntities().size() > 0) {
link.setSource(entityCollection.getEntities().get(0).getId());
link.setRelationship("NavPropertyETAllPrimOne");
link.setTarget(data.get("ESAllPrim").getEntities().get(1).getId());
}
listOfAddedLinks.add(link);
return listOfAddedLinks;
}
public List<DeltaLink> readDeletedLinks(final EdmEntitySet edmEntitySet) throws DataProviderException {
EntityCollection entityCollection = data.get(edmEntitySet.getName());
List<DeltaLink> listOfDeletedLinks = new ArrayList<DeltaLink>();
if (entityCollection.getEntities().size() > 1) {
DeltaLink link = new DeltaLink();
link.setSource(entityCollection.getEntities().get(0).getId());
link.setRelationship("NavPropertyETAllPrimOne");
link.setTarget(data.get("ESAllPrim").getEntities().get(0).getId());
listOfDeletedLinks.add(link);
link = new DeltaLink();
link.setSource(entityCollection.getEntities().get(1).getId());
link.setRelationship("NavPropertyETAllPrimOne");
link.setTarget(data.get("ESAllPrim").getEntities().get(1).getId());
listOfDeletedLinks.add(link);
}
return listOfDeletedLinks;
}
public EntityCollection readFunctionEntityCollection(final EdmFunction function, final List<UriParameter> parameters,
final UriInfoResource uriInfo) throws DataProviderException {
return FunctionData.entityCollectionFunction(function.getName(),
@ -713,6 +763,42 @@ public class DataProvider {
}
}
public List<Entity> readNavigationEntities(EdmEntitySet entitySet) {
EntityCollection entityCollection = data.get(entitySet.getName());
List<Entity> entities = new ArrayList<Entity>();
Entity otherEntity = entitySet.getName() == "ESAllPrim" ? data.get("ESDelta").getEntities().get(0) :
data.get("ESAllPrim").getEntities().get(0);
EntityCollection ec1=new EntityCollection();
Entity entity1 = new Entity();
entity1.setId(entityCollection.getEntities().get(0).getId());//added navigation
entity1.addProperty(entityCollection.getEntities().get(0).getProperty(edm.getEntityContainer()
.getEntitySet(entitySet.getName()).getEntityType().getPropertyNames().get(0)));
Link link = new Link();
Entity entity2 = new Entity();
entity2.setId(otherEntity.getId());
ec1.getEntities().add(entity2);
link.setInlineEntitySet(ec1);
link.setTitle("NavPropertyETAllPrimMany");
entity1.getNavigationLinks().add(link);
Entity entity3 = new Entity();
EntityCollection ec2=new EntityCollection();
entity3.setId(entityCollection.getEntities().get(0).getId());//added navigation
DeletedEntity delentity = new DeletedEntity();
delentity.setId(otherEntity.getId());
delentity.setReason(Reason.deleted);
ec2.getEntities().add(delentity);
Link delLink = new Link();
delLink.setInlineEntitySet(ec2);
delLink.setTitle("NavPropertyETAllPrimMany");
entity3.getNavigationLinks().add(delLink);
entities.add(otherEntity);
entities.add(entity1);
entities.add(entity3);
return entities;
}
public Entity read(EdmSingleton singleton) {
if (data.containsKey(singleton.getName())) {
EntityCollection entitySet = data.get(singleton.getName());

View File

@ -19,7 +19,9 @@
package org.apache.olingo.server.tecsvc.processor;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@ -28,6 +30,9 @@ import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Builder;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.Delta;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
@ -62,6 +67,7 @@ import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
@ -70,12 +76,14 @@ import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.IdOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
import org.apache.olingo.server.tecsvc.async.AsyncProcessor;
import org.apache.olingo.server.tecsvc.async.TechnicalAsyncService;
import org.apache.olingo.server.tecsvc.data.DataProvider;
import org.apache.olingo.server.tecsvc.data.RequestValidator;
import org.apache.olingo.server.tecsvc.processor.queryoptions.ExpandSystemQueryOptionHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.CountHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.DeltaTokenHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.OrderByHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SearchHandler;
@ -91,6 +99,8 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
implements EntityCollectionProcessor, CountEntityCollectionProcessor, EntityProcessor, MediaEntityProcessor,
ReferenceCollectionProcessor, ReferenceProcessor {
private static final String DELTATOKEN = "deltatoken";
public TechnicalEntityProcessor(final DataProvider dataProvider, final ServiceMetadata serviceMetadata) {
super(dataProvider, serviceMetadata);
}
@ -110,15 +120,28 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
getEdmEntitySet(uriInfo); // including checks
final EntityCollection entitySetInitial = readEntityCollection(uriInfo);
EntityCollection entitySet = new EntityCollection();
entitySet.getEntities().addAll(entitySetInitial.getEntities());
FilterHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), entitySet, uriInfo, serviceMetadata.getEdm());
response.setContent(odata.createFixedFormatSerializer().count(
entitySet.getEntities().size()));
int count = entitySet.getEntities().size();
for (SystemQueryOption systemQueryOption : uriInfo.getSystemQueryOptions()) {
if (systemQueryOption.getName().contains(DELTATOKEN)) {
count = count + getDeltaCount(uriInfo);
break;
}
}
response.setContent(odata.createFixedFormatSerializer().count(count));
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString());
}
private int getDeltaCount(UriInfo uriInfo) throws ODataApplicationException {
List<DeletedEntity> deletedEntity = readDeletedEntities(uriInfo);
List<DeltaLink> addedLink = readAddedLinks(uriInfo);
List<DeltaLink> deletedLink = readDeletedLinks(uriInfo);
List<Entity> listofNavigationEntities = readNavigationEntities(uriInfo);
return deletedEntity.size() + addedLink.size() + deletedLink.size() + listofNavigationEntities.size();
}
@Override
public void readEntity(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo,
final ContentType requestedContentType) throws ODataApplicationException, ODataLibraryException {
@ -513,6 +536,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
edmEntitySet.getEntityType();
EntityCollection entitySetInitial = readEntityCollection(uriInfo);
Delta delta = null;
if (entitySetInitial == null) {
entitySetInitial = new EntityCollection();
}
@ -553,7 +577,35 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand, uriInfo,
serviceMetadata.getEdm());
final CountOption countOption = uriInfo.getCountOption();
final List<SystemQueryOption> systemQueryOptions = uriInfo.getSystemQueryOptions();
String deltaToken = null;
for (SystemQueryOption systemQueryOption : systemQueryOptions) {
if (systemQueryOption.getName().contains(DELTATOKEN)) {
deltaToken = systemQueryOption.getText();
delta = new Delta();
Integer count = 0;
if (deltaToken != null) {
String deltaTokenValue = generateDeltaToken();
List<DeletedEntity> listOfDeletedEntities = readDeletedEntities(uriInfo);
List<DeltaLink> listOfAddedLinks = readAddedLinks(uriInfo);
List<DeltaLink> listOfDeletedLinks = readDeletedLinks(uriInfo);
List<Entity> listofNavigationEntities = readNavigationEntities(uriInfo);
delta.getDeletedEntities().addAll(listOfDeletedEntities);
delta.getAddedLinks().addAll(listOfAddedLinks);
delta.getDeletedLinks().addAll(listOfDeletedLinks);
delta.getEntities().addAll(listofNavigationEntities);
count = listOfDeletedLinks.size()+listOfAddedLinks.size()+listOfDeletedEntities.size();
delta.setDeltaLink(DeltaTokenHandler.createDeltaLink(
request.getRawRequestUri(),
deltaTokenValue));
}
delta.getEntities().addAll(entitySetSerialization.getEntities());
count = count + delta.getEntities().size();
delta.setCount(count);
break;
}
}
String id;
if (edmEntitySet == null) {
// Used for functions, function imports etc.
@ -561,7 +613,12 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
} else {
id = request.getRawBaseUri() + edmEntitySet.getName();
}
if(odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).hasTrackChanges()) {
String deltaTokenValue = generateDeltaToken();
entitySetSerialization.setDeltaLink(DeltaTokenHandler.createDeltaLink(
request.getRawRequestUri(),
deltaTokenValue));
}
if(isReference) {
final SerializerResult serializerResult =
serializeReferenceCollection(entitySetSerialization, edmEntitySet, requestedContentType, countOption);
@ -573,6 +630,12 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
expand, select, countOption, id);
response.setODataContent(serializerResult.getODataContent());
} else if(delta != null){
final SerializerResult serializerResult =
serializeDeltaPayloads(request,
delta, edmEntitySet, edmEntityType, requestedContentType,
expand, select, countOption, id);
response.setContent(serializerResult.getContent());
} else {
final SerializerResult serializerResult =
serializeEntityCollection(request,
@ -587,8 +650,44 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
if (pageSize != null) {
response.setHeader(HttpHeader.PREFERENCE_APPLIED,
PreferencesApplied.with().maxPageSize(serverPageSize).build().toValueString());
}else if (odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).hasTrackChanges()) {
response.setHeader(HttpHeader.PREFERENCE_APPLIED,
PreferencesApplied.with().trackChanges().build().toValueString());
}
if(delta!=null){
response.setHeader(HttpHeader.ODATA_VERSION,request.getHeaders(HttpHeader.ODATA_MAX_VERSION).get(0));
}
}
private List<Entity> readNavigationEntities(final UriInfo uriInfo) {
final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
return dataProvider.readNavigationEntities(((UriResourceEntitySet) resourcePaths.get(0)).getEntitySet());
}
private String generateDeltaToken() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.000");
Date date = new Date(System.currentTimeMillis());
return dateFormat.format(date);
}
private SerializerResult serializeDeltaPayloads(final ODataRequest request, final Delta delta,
final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType,
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
final CountOption countOption, String id) throws ODataLibraryException {
return odata.createEdmDeltaSerializer(requestedFormat, request.getHeaders(HttpHeader.ODATA_VERSION))
.entityCollection(serviceMetadata,
edmEntityType, delta,
EntityCollectionSerializerOptions.with()
.contextURL(isODataMetadataNone(requestedFormat) ? null :
getContextUrl(request.getRawODataPath(), edmEntitySet, edmEntityType, false, expand, select))
.count(countOption)
.expand(expand).select(select)
.id(id)
.build());
}
/**
* Check is streaming is enabled for this entity set in combination with the given content type.

View File

@ -21,6 +21,8 @@ package org.apache.olingo.server.tecsvc.processor;
import java.util.List;
import java.util.Locale;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
@ -231,6 +233,23 @@ public abstract class TechnicalProcessor implements Processor {
return entitySet;
}
protected List<DeletedEntity> readDeletedEntities(final UriInfoResource uriInfo) throws ODataApplicationException {
final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
return dataProvider.readDeletedEntities(((UriResourceEntitySet) resourcePaths.get(0)).getEntitySet());
}
protected List<DeltaLink> readAddedLinks(final UriInfoResource uriInfo) throws ODataApplicationException {
final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
return dataProvider.readAddedLinks(((UriResourceEntitySet) resourcePaths.get(0)).getEntitySet());
}
protected List<DeltaLink> readDeletedLinks(final UriInfoResource uriInfo) throws ODataApplicationException {
final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
return dataProvider.readDeletedLinks(((UriResourceEntitySet) resourcePaths.get(0)).getEntitySet());
}
protected EntityCollection readEntityCollection(final UriInfoResource uriInfo) throws ODataApplicationException {
final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
if (resourcePaths.size() > 1 && resourcePaths.get(1) instanceof UriResourceNavigation) {

View File

@ -0,0 +1,54 @@
/*
* 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.server.tecsvc.processor.queryoptions.options;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
public class DeltaTokenHandler {
public static URI createDeltaLink(final String rawRequestUri, String deltaToken)
throws ODataApplicationException {
// Remove a maybe existing skiptoken, making sure that the query part is not empty.
String deltalink = rawRequestUri.contains("?") ?
rawRequestUri.replaceAll("(\\$|%24)deltatoken=.+&?", "").replaceAll("(\\?|&)$", "") :
rawRequestUri;
// Add a question mark or an ampersand, depending on the current query part.
deltalink += deltalink.contains("?") ? '&' : '?';
deltaToken = deltaToken.substring(0, 4);
// Append the new skiptoken.
deltalink += SystemQueryOptionKind.DELTATOKEN.toString().replace("$", "%24") // poor man's percent encoding
+ '='
+ "%2A" +deltaToken ; // "%2A" is a percent-encoded asterisk
try {
return new URI(deltalink);
} catch (final URISyntaxException e) {
throw new ODataApplicationException("Exception while constructing delta link",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e);
}
}
}

View File

@ -109,6 +109,7 @@ public class ContainerProvider {
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESPeople"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESTwoPrimDerived"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESAllPrimDerived"));
entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESDelta"));
// Singletons
List<CsdlSingleton> singletons = new ArrayList<CsdlSingleton>();
@ -678,7 +679,7 @@ public class ContainerProvider {
new CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression(
new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, "true"))));
}else if (name.equals("ESAllPrimDerived")) {
} else if (name.equals("ESAllPrimDerived")) {
return new CsdlEntitySet()
.setName("ESAllPrimDerived")
.setType(EntityTypeProvider.nameETAllPrim)
@ -692,6 +693,17 @@ public class ContainerProvider {
new CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression(
new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, "true"))
));
} else if (name.equals("ESDelta")) {
return new CsdlEntitySet()
.setName("ESDelta")
.setType(EntityTypeProvider.nameETTwoPrim)
.setNavigationPropertyBindings(Arrays.asList(
new CsdlNavigationPropertyBinding()
.setPath("NavPropertyETAllPrimOne")
.setTarget("ESAllPrim"),
new CsdlNavigationPropertyBinding()
.setPath("NavPropertyETAllPrimMany")
.setTarget("ESAllPrim")));
}
}

View File

@ -87,6 +87,8 @@ public class EntityTypeProvider {
"ETMixEnumDefCollComp");
public static final FullQualifiedName nameETStream = new FullQualifiedName(SchemaProvider.NAMESPACE,
"ETWithStream");
public static final FullQualifiedName nameETDelta =
new FullQualifiedName(SchemaProvider.NAMESPACE, "ETDelta");
public static final FullQualifiedName nameETPeople = new FullQualifiedName(SchemaProvider.NAMESPACE, "ETPeople");
@ -503,7 +505,16 @@ public class EntityTypeProvider {
PropertyProvider.propertyId,
PropertyProvider.propertyName))
.setNavigationProperties(Arrays.asList(PropertyProvider.navPropertyFriends));
}
} else if (entityTypeName.equals(nameETDelta)) {
return new CsdlEntityType()
.setName("ETDelta")
.setBaseType(nameETTwoPrim)
.setProperties(Arrays.asList(
PropertyProvider.propertyInt16_NotNullable, PropertyProvider.propertyString))
.setNavigationProperties(
Arrays.asList(PropertyProvider.navPropertyETAllPrimOne_ETAllPrim,
PropertyProvider.collectionNavPropertyETAllPrimMany_ETAllPrim));
}
return null;
}
}

View File

@ -86,6 +86,7 @@ public class SchemaProvider {
entityTypes.add(prov.getEntityType(EntityTypeProvider.nameETCompMixPrimCollComp));
entityTypes.add(prov.getEntityType(EntityTypeProvider.nameETKeyPrimNav));
entityTypes.add(prov.getEntityType(EntityTypeProvider.nameETStream));
entityTypes.add(prov.getEntityType(EntityTypeProvider.nameETDelta));
// ComplexTypes
List<CsdlComplexType> complexTypes = new ArrayList<CsdlComplexType>();

View File

@ -0,0 +1,731 @@
/*
* 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.server.core.serializer.json;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.DeletedEntity;
import org.apache.olingo.commons.api.data.DeletedEntity.Reason;
import org.apache.olingo.commons.api.data.Delta;
import org.apache.olingo.commons.api.data.DeltaLink;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edmx.EdmxReference;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.uri.UriHelper;
import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.ExpandSelectMock;
import org.apache.olingo.server.tecsvc.MetadataETagSupport;
import org.apache.olingo.server.tecsvc.data.DataProvider;
import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class JsonDeltaSerializerTest {
final EdmDeltaSerializer ser;
private static final OData odata = OData.newInstance();
private static final ServiceMetadata metadata = odata.createServiceMetadata(
new EdmTechProvider(), Collections.<EdmxReference> emptyList(), new MetadataETagSupport("W/\"metadataETag\""));
private static final EdmEntityContainer entityContainer = metadata.getEdm().getEntityContainer();
private final DataProvider data = new DataProvider(odata, metadata.getEdm());
public JsonDeltaSerializerTest() throws SerializerException {
List<String> versions = new ArrayList<String>();
versions.add("4.0");
ser = OData.newInstance().createEdmDeltaSerializer(ContentType.JSON, versions);
}
@Test
public void addedDeltaLink() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void deletedDeltaLink() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> deletedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
deletedLinks.add(link1 );
delta.getDeletedLinks().addAll(deletedLinks);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$deletedLink\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void deletedEntity() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeletedEntity> deletedEntity = new ArrayList<DeletedEntity>();
DeletedEntity entity1 = new DeletedEntity();
entity1.setId(new URI("ESDelta(100)"));
entity1.setReason(Reason.deleted);
DeletedEntity entity2 = new DeletedEntity();
entity2.setId(new URI("ESDelta(-32768)"));
entity2.setReason(Reason.changed);
deletedEntity.add(entity1);
deletedEntity.add(entity2);
delta.getDeletedEntities().addAll(deletedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+"\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta(100)/$deletedEntity\","
+ "\"@odata.id\":\"ESDelta(100)\",\"reason\":\"deleted\"},{"
+ "\"@odata.context\":\"#ESDelta(-32768)/$deletedEntity\","
+ "\"@odata.id\":\"ESDelta(-32768)\",\"reason\":\"changed\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void addedChangedDeltaEntity() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
final Entity entity2 = data.readAll(edmEntitySet).getEntities().get(1);
List<Entity> addedEntity = new ArrayList<Entity>();
Entity changedEntity = new Entity();
changedEntity.setId(entity2.getId());
changedEntity.addProperty(entity2.getProperty("PropertyString"));
addedEntity.add(entity);
addedEntity.add(changedEntity);
delta.getEntities().addAll(addedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.id\":\"ESDelta(32767)\",\"PropertyInt16\":32767,"
+ "\"PropertyString\":\"Number:32767\"},{\"@odata.id\":\"ESDelta(-32768)\","
+ "\"PropertyString\":\"Number:-32768\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void basicDeltaTest() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
List<DeltaLink> deletedLinks = new ArrayList<DeltaLink>();
DeltaLink delLink = new DeltaLink();
delLink.setRelationship("NavPropertyETAllPrimOne");
delLink.setSource(new URI("ESDelta(100)"));
delLink.setTarget(new URI("ESAllPrim(0)"));
deletedLinks.add(delLink );
delta.getDeletedLinks().addAll(deletedLinks);
List<DeletedEntity> deletedEntity = new ArrayList<DeletedEntity>();
DeletedEntity delEntity1 = new DeletedEntity();
delEntity1.setId(new URI("ESDelta(100)"));
delEntity1.setReason(Reason.deleted);
DeletedEntity delEntity2 = new DeletedEntity();
delEntity2.setId(new URI("ESDelta(-32768)"));
delEntity2.setReason(Reason.changed);
deletedEntity.add(delEntity1);
deletedEntity.add(delEntity2);
delta.getDeletedEntities().addAll(deletedEntity);
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
final Entity entity2 = data.readAll(edmEntitySet).getEntities().get(1);
List<Entity> addedEntity = new ArrayList<Entity>();
Entity changedEntity = new Entity();
changedEntity.setId(entity2.getId());
changedEntity.addProperty(entity2.getProperty("PropertyString"));
addedEntity.add(entity);
addedEntity.add(changedEntity);
delta.getEntities().addAll(addedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+"\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.id\":\"ESDelta(32767)\",\"PropertyInt16\":32767,"
+ "\"PropertyString\":\"Number:32767\"},{\"@odata.id\":\"ESDelta(-32768)\","
+ "\"PropertyString\":\"Number:-32768\"},{\"@odata.context\":\"#ESDelta(100)/$deletedEntity\","
+ "\"@odata.id\":\"ESDelta(100)\",\"reason\":\"deleted\"},"
+ "{\"@odata.context\":\"#ESDelta(-32768)/$deletedEntity\",\"@odata.id\":\"ESDelta(-32768)\","
+ "\"reason\":\"changed\"},{\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\",\"target\":\"ESAllPrim(0)\"},{\"@odata.context\":"
+ "\"#ESDelta/$deletedLink\",\"source\":\"ESDelta(100)\",\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void addedDifferentdDeltaEntity() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
final EdmEntitySet edmEntitySet2 = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
List<Entity> addedEntity = new ArrayList<Entity>();
addedEntity.add(entity);
delta.getEntities().addAll(addedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet2.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet2).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+"\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESAllPrim/$entity\",\"@odata.id\":\"ESAllPrim(32767)\","
+ "\"PropertyInt16\":32767,\"PropertyString\":\"First Resource - positive values\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testDeltaToken() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
delta.setDeltaLink(new URI("23042017"));
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}],"
+ "\"@odata.deltaLink\":\"23042017\""
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testSkipToken() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
delta.setNext(new URI("23042017"));
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}],"
+ "\"@odata.nextLink\":\"23042017\""
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testSkipDeltaToken() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
delta.setNext(new URI("23042017"));
delta.setDeltaLink(new URI("02052017"));
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}],"
+ "\"@odata.nextLink\":\"23042017\""
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testDeltaCount() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
CountOption countOption = Mockito.mock(CountOption.class);
Mockito.when(countOption.getValue()).thenReturn(true);
Delta delta = new Delta();
delta.setCount(1);
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
delta.setDeltaLink(new URI("23042017"));
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.count(countOption)
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\","
+ "\"@odata.count\":\"1\","
+ "\"value\":[{"
+ "\"@odata.context\":\"#ESDelta/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}],"
+ "\"@odata.deltaLink\":\"23042017\""
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testEmptyDelta() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":[]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testDeltaForStream() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESWithStream");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESWithStream/$delta\","
+ "\"value\":[{\"@odata.context\":\"#ESWithStream/$link\","
+ "\"source\":\"ESDelta(100)\",\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void selectInDelta() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
final EdmEntityType entityType = edmEntitySet.getEntityType();
final UriHelper helper = odata.createUriHelper();
final SelectOption select = ExpandSelectMock.mockSelectOption(Collections.singletonList(
ExpandSelectMock.mockSelectItem(entityContainer.getEntitySet("ESAllPrim"), "PropertyString")));
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
final Entity entity2 = data.readAll(edmEntitySet).getEntities().get(1);
Delta delta = new Delta();
List<Entity> addedEntity = new ArrayList<Entity>();
Entity changedEntity = new Entity();
changedEntity.setId(entity2.getId());
changedEntity.addProperty(entity2.getProperty("PropertyString"));
changedEntity.addProperty(entity2.getProperty("PropertyInt16"));
addedEntity.add(entity);
addedEntity.add(changedEntity);
delta.getEntities().addAll(addedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, null, select))
.suffix(Suffix.ENTITY).build())
.select(select).build()).getContent();
String jsonString = IOUtils.toString(stream);
Assert.assertEquals("{"
+"\"@odata.context\":\"$metadata#ESDelta(PropertyString)/$entity/$delta\","
+ "\"value\":[{\"@odata.id\":\"ESDelta(32767)\",\"PropertyString\":\"Number:32767\"},"
+ "{\"@odata.id\":\"ESDelta(-32768)\",\"PropertyString\":\"Number:-32768\"}]}",
jsonString);
}
@Test
public void testCollPropertyInDelta() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
List<Entity> addedEntities = new ArrayList<Entity>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
addedEntities.add(entity);
delta.getAddedLinks().addAll(addedLinks );
delta.getEntities().addAll(addedEntities);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESCollAllPrim/$delta\","
+ "\"value\":[{\"@odata.id\":\"ESCollAllPrim(1)\",\"PropertyInt16\":1,"
+ "\"CollPropertyString\":[\"Employee1@company.example\",\"Employee2@company.example\","
+ "\"Employee3@company.example\"],\"CollPropertyBoolean\":[true,false,true],"
+ "\"CollPropertyByte\":[50,200,249],\"CollPropertySByte\":[-120,120,126],\"CollPropertyInt16\":"
+ "[1000,2000,30112],\"CollPropertyInt32\":[23232323,11223355,10000001],\"CollPropertyInt64\":"
+ "[929292929292,333333333333,444444444444],\"CollPropertySingle\":[1790.0,26600.0,3210.0],"
+ "\"CollPropertyDouble\":[-17900.0,-2.78E7,3210.0],\"CollPropertyDecimal\":"
+ "[12,-2,1234],\"CollPropertyBinary\":"
+ "[\"q83v\",\"ASNF\",\"VGeJ\"],\"CollPropertyDate\":[\"1958-12-03\",\"1999-08-05\",\"2013-06-25\"],"
+ "\"CollPropertyDateTimeOffset\":[\"2015-08-12T03:08:34Z\",\"1970-03-28T12:11:10Z\","
+ "\"1948-02-17T09:09:09Z\"],"
+ "\"CollPropertyDuration\":[\"PT13S\",\"PT5H28M0S\",\"PT1H0S\"],\"CollPropertyGuid\":"
+ "[\"ffffff67-89ab-cdef-0123-456789aaaaaa\",\"eeeeee67-89ab-cdef-0123-456789bbbbbb\","
+ "\"cccccc67-89ab-cdef-0123-456789cccccc\"],\"CollPropertyTimeOfDay\":[\"04:14:13\",\"23:59:59\","
+ "\"01:12:33\"]},{\"@odata.context\":\"#ESCollAllPrim/$link\",\"source\":\"ESDelta(100)\",\"relationship\":"
+ "\"NavPropertyETAllPrimOne\",\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void testComplexCollPropertyInDelta() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESKeyNav");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
List<Entity> addedEntities = new ArrayList<Entity>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
addedEntities.add(entity);
delta.getAddedLinks().addAll(addedLinks );
delta.getEntities().addAll(addedEntities);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+"\"@odata.context\":\"$metadata#ESKeyNav/$delta\","
+ "\"value\":[{\"@odata.id\":\"ESKeyNav(1)\",\"PropertyInt16\":1,"
+ "\"PropertyString\":\"I am String Property 1\",\"PropertyCompNav\":"
+ "{\"PropertyInt16\":1},\"PropertyCompAllPrim\":{\"PropertyString\":"
+ "\"First Resource - positive values\",\"PropertyBinary\":"
+ "\"ASNFZ4mrze8=\",\"PropertyBoolean\":true,\"PropertyByte\":"
+ "255,\"PropertyDate\":\"2012-12-03\",\"PropertyDateTimeOffset\":"
+ "\"2012-12-03T07:16:23Z\",\"PropertyDecimal\":"
+ "34,\"PropertySingle\":1.79E20,\"PropertyDouble\":-1.79E20,\"PropertyDuration\":\"PT6S\",\"PropertyGuid\":"
+ "\"01234567-89ab-cdef-0123-456789abcdef\",\"PropertyInt16\":32767,"
+ "\"PropertyInt32\":2147483647,\"PropertyInt64\":"
+ "9223372036854775807,\"PropertySByte\":127,\"PropertyTimeOfDay\":\"21:05:59\"},\"PropertyCompTwoPrim\":"
+ "{\"PropertyInt16\":16,\"PropertyString\":\"Test123\"},\"CollPropertyString\":"
+ "[\"Employee1@company.example\","
+ "\"Employee2@company.example\",\"Employee3@company.example\"],\"CollPropertyInt16\":[1000,2000,30112],"
+ "\"CollPropertyComp\":[{\"PropertyInt16\":1,\"PropertyComp\":{\"PropertyString\":"
+ "\"First Resource - positive values\","
+ "\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyBoolean\":true,\"PropertyByte\":"
+ "255,\"PropertyDate\":\"2012-12-03\","
+ "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\",\"PropertyDecimal\":34,\"PropertySingle\":1.79E20,"
+ "\"PropertyDouble\":-1.79E20,\"PropertyDuration\":\"PT6S\",\"PropertyGuid\":"
+ "\"01234567-89ab-cdef-0123-456789abcdef\","
+ "\"PropertyInt16\":32767,\"PropertyInt32\":2147483647,\"PropertyInt64\":"
+ "9223372036854775807,\"PropertySByte\":127,"
+ "\"PropertyTimeOfDay\":\"21:05:59\"}},{\"PropertyInt16\":2,\"PropertyComp\":{\"PropertyString\":"
+ "\"First Resource - positive values\",\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyBoolean\":true,"
+ "\"PropertyByte\":255,\"PropertyDate\":\"2012-12-03\",\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+ "\"PropertyDecimal\":34,\"PropertySingle\":1.79E20,\"PropertyDouble\":-1.79E20,"
+ "\"PropertyDuration\":\"PT6S\","
+ "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+ "\"PropertyInt16\":32767,\"PropertyInt32\":2147483647,"
+ "\"PropertyInt64\":9223372036854775807,\"PropertySByte\":127,"
+ "\"PropertyTimeOfDay\":\"21:05:59\"}},{\"PropertyInt16\":3,"
+ "\"PropertyComp\":{\"PropertyString\":\"First Resource - positive values\","
+ "\"PropertyBinary\":\"ASNFZ4mrze8=\","
+ "\"PropertyBoolean\":true,\"PropertyByte\":255,\"PropertyDate\":\"2012-12-03\",\"PropertyDateTimeOffset\":"
+ "\"2012-12-03T07:16:23Z\",\"PropertyDecimal\":34,\"PropertySingle\":1.79E20,\"PropertyDouble\":-1.79E20,"
+ "\"PropertyDuration\":\"PT6S\",\"PropertyGuid\":"
+ "\"01234567-89ab-cdef-0123-456789abcdef\",\"PropertyInt16\":32767,"
+ "\"PropertyInt32\":2147483647,\"PropertyInt64\":9223372036854775807,"
+ "\"PropertySByte\":127,\"PropertyTimeOfDay\":"
+ "\"21:05:59\"}}],\"PropertyCompCompNav\":{\"PropertyString\":\"1\","
+ "\"PropertyCompNav\":{\"PropertyInt16\":1}}},"
+ "{\"@odata.context\":\"#ESKeyNav/$link\",\"source\":\"ESDelta(100)\","
+ "\"relationship\":\"NavPropertyETAllPrimOne\","
+ "\"target\":\"ESAllPrim(0)\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test
public void navigationEntityInDeltaEntity() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
final Entity entity2 = data.readAll(edmEntitySet).getEntities().get(3);
final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList(
ExpandSelectMock.mockExpandItem(edmEntitySet, "NavPropertyETAllPrimOne")));
List<Entity> addedEntity = new ArrayList<Entity>();
Entity changedEntity = new Entity();
changedEntity.setId(entity.getId());
changedEntity.addProperty(entity.getProperty("PropertyString"));
addedEntity.add(entity);
addedEntity.add(entity2);
delta.getEntities().addAll(addedEntity);
InputStream stream = ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build()).expand(expand)
.build()).getContent();
String jsonString = IOUtils.toString(stream);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESDelta/$delta\",\"value\":"
+ "[{\"@odata.id\":\"ESDelta(32767)\",\"PropertyInt16\":32767,\"PropertyString\":"
+ "\"Number:32767\"},{\"@odata.id\":\"ESDelta(100)\",\"PropertyInt16\":100,"
+ "\"PropertyString\":\"Number:100\"}]"
+ "}";
Assert.assertNotNull(jsonString);
Assert.assertEquals(expectedResult, jsonString);
}
@Test(expected = SerializerException.class)
public void negativeDeltaEntityTest() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
final Entity entity2 = data.readAll(edmEntitySet).getEntities().get(1);
List<Entity> addedEntity = new ArrayList<Entity>();
Entity changedEntity = new Entity();
changedEntity.addProperty(entity2.getProperty("PropertyString"));
addedEntity.add(changedEntity);
delta.getEntities().addAll(addedEntity);
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeLinkDeltaTest1() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setSource(new URI("ESDelta(100)"));
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeLinkDeltaTest2() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setTarget(new URI("ESAllPrim(0)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeLinkDeltaTest3() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = new DeltaLink();
link1.setRelationship("NavPropertyETAllPrimOne");
link1.setSource(new URI("ESDelta(100)"));
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeLinkDeltaTest4() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeltaLink> addedLinks = new ArrayList<DeltaLink>();
DeltaLink link1 = null;
addedLinks.add(link1 );
delta.getAddedLinks().addAll(addedLinks );
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeDeltaDeletedEntityTest1() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeletedEntity> deletedEntity = new ArrayList<DeletedEntity>();
DeletedEntity delEntity1 = new DeletedEntity();
delEntity1.setReason(Reason.deleted);
deletedEntity.add(delEntity1);
delta.getDeletedEntities().addAll(deletedEntity);
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
@Test(expected = SerializerException.class)
public void negativeDeltaDeletedEntityTest2() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESDelta");
Delta delta = new Delta();
List<DeletedEntity> deletedEntity = new ArrayList<DeletedEntity>();
DeletedEntity delEntity1 = new DeletedEntity();
delEntity1.setId(new URI("ESDelta(100)"));
deletedEntity.add(delEntity1);
delta.getDeletedEntities().addAll(deletedEntity);
ser.entityCollection(metadata, edmEntitySet.getEntityType(), delta ,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getContent();
}
}