package org.odata4j.producer.resources;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import org.odata4j.core.ODataConstants;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityId;
import org.odata4j.core.OEntityIds;
import org.odata4j.core.OEntityKey;
import org.odata4j.format.FormatParser;
import org.odata4j.format.FormatParserFactory;
import org.odata4j.format.SingleLink;
import org.odata4j.producer.ODataProducer;
import org.odata4j.urlencoder.ConversionUtil;
/**
*
* Copyright 2013 Halliburton
* @author <a href="mailto:peng.chen@halliburton.com">Kevin Chen</a>
*
*/
public abstract class ODataBatchSingleUnit extends ODataBatchUnit {
final private UriInfo uriInformation;
// this is the uri for individual batch operation,
final private URI fullResourceUri;
// after parsing the full uri, the query string will be put into the map
private MultivaluedMap<String, String> queryMap = null;
// the entity name corresponding to the table name
private String entitySetName;
// if uri contain the primary key to locate a entity
private String entityKey;
// navigation property
private String navProperty;
final private String resourceContents;
final private MultivaluedMap<String, String> resourceHeaders;
private boolean isParsed = false;
private boolean hasLinkProperty = false;
private String linkTargetProperty;
private String linkTargetId;
private Response intermediateResponse;
/**
* Gets the intermediate response.
*
* @return the intermediate response
*/
public Response getIntermediateResponse() {
return intermediateResponse;
}
/**
* Sets the intermediate response.
*
* @param intermediateResponse the new intermediate response
*/
public void setIntermediateResponse(Response intermediateResponse) {
this.intermediateResponse = intermediateResponse;
}
protected abstract Response delegate(HttpHeaders httpHeaders, URI baseUri, ContextResolver<ODataProducer> producerResolver) throws Exception;
protected ODataBatchSingleUnit(UriInfo uriInfo, String uri, String contents, MultivaluedMap<String, String> headers) throws URISyntaxException {
uriInformation = uriInfo;
fullResourceUri = new URI(uri);
resourceContents = contents;
resourceHeaders = headers;
}
protected String createResponse(Response response) {
StringBuilder batchResponse = new StringBuilder();
batchResponse.append("\n").append(ODataConstants.Headers.CONTENT_TYPE).append(": application/http");
batchResponse.append("\nContent-Transfer-Encoding: binary\n");
batchResponse.append(BatchRequestResource.createResponseBodyPart(this, response));
return batchResponse.toString();
}
@Override
public final Response execute(HttpHeaders httpHeaders, ContextResolver<ODataProducer> producerResolver, URI baseUri) throws Exception {
if (!fullResourceUri.toString().startsWith(baseUri.toString())) {
throw new UnsupportedOperationException("the resouce url does not match base url from batch operation,\n\tbaseUri=" + baseUri +
"\n\trequest url=" + fullResourceUri);
}
if (!isParsed) {
parseUri(fullResourceUri, baseUri);
isParsed = true;
}
return delegate(httpHeaders, baseUri, producerResolver);
}
/**
* Creates the response for batch.
*
* @param httpHeaders the http headers
* @param producerResolver the producer resolver
* @param baseUri the base uri
* @param requestedEntity the requested entity
* @return the response
* @throws Exception the exception
*/
public Response createResponseForBatch(HttpHeaders httpHeaders, ContextResolver<ODataProducer> producerResolver, URI baseUri, String requestedEntity) throws Exception {
ODataProducer producer = producerResolver.getContext(ODataProducer.class);
OEntityKey entityKey = null;
//setting isResponse to true, since the entity we get from the response contain not only name and value pairs, but also metadata, relations,etc
//so that Json parser will parse it and give you back OEntity.
Boolean isResponse = true;
BatchRequestResource batchRequestResource = new BatchRequestResource();
OEntity entity = batchRequestResource.getRequestEntity(httpHeaders, getResourceHeaders(), getUriInfo(), requestedEntity, producer.getMetadata(), entitySetName, entityKey, isResponse);
return batchRequestResource.createResponseForBatch(httpHeaders, getUriInfo(), producer, getEnitySetName(), entity, getMediaTypeListForBatch());
}
public UriInfo getUriInfo() {
return uriInformation;
}
public URI getFullResourceUri() {
return fullResourceUri;
}
public MultivaluedMap<String, String> getQueryStringsMap() {
return queryMap;
}
public String getEnitySetName() {
return entitySetName;
}
public String getEntityKey() {
return entityKey;
}
public String getResourceContent() {
return resourceContents;
}
public MultivaluedMap<String, String> getResourceHeaders() {
return resourceHeaders;
}
public MediaType getResourceMediaType() {
String contentType = resourceHeaders.getFirst(ODataConstants.Headers.CONTENT_TYPE);
MediaType type = BatchRequestResource.getMediaType(contentType);
return type;
}
/**
* Gets the media type list for batch.
*
* @return the media type list for batch
*/
public List<MediaType> getMediaTypeListForBatch() {
List<MediaType> mediaTypeList = null;
MediaType mediaType = getResourceMediaType();
if (mediaType != null) {
mediaTypeList = new ArrayList<MediaType>();
mediaTypeList.add(mediaType);
}
return mediaTypeList;
}
/**
* @return the navProperty
*/
public String getNavProperty() {
return navProperty;
}
public boolean isEntityCountRequest() {
if (navProperty != null && navProperty.toLowerCase().equals("$count")) {
return true;
}
return false;
}
public boolean hasLinkProperty() {
return hasLinkProperty;
}
public String getLinkTargetProperty() {
return linkTargetProperty;
}
public String getLinkTargetId() {
return linkTargetId;
}
protected LinksRequestResource getLinkRequestResouce() {
if (!hasLinkProperty) {
throw new UnsupportedOperationException("the request is not a link request: " + fullResourceUri);
}
OEntityKey targetEntityKey = linkTargetId == null || linkTargetId.isEmpty() ? null : OEntityKey.parse(linkTargetId);
return new LinksRequestResource(OEntityIds.create(entitySetName, OEntityKey.parse(entityKey)), linkTargetProperty, targetEntityKey);
}
@Override
public String getBatchUnitContentType() {
return "\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary";
}
/**
* parse the request uri to create query String Map
* @param baseUri
* @return
* @throws URISyntaxException
*/
private void parseUri(URI fullUri, URI baseUri) {
// $link in odata spec
String linkPatten = "$links/";
// get the query string from the request
queryMap = ConversionUtil.decodeQueryString(fullResourceUri);
// get the entity/navigation part without query string
URI relUri = baseUri.relativize(fullUri);
String entityUriString = relUri.getPath();
// remove starting / if existed
if (entityUriString.startsWith("/")) {
entityUriString = entityUriString.substring(1);
}
int i = entityUriString.indexOf('/');
if (i != -1) {
entitySetName = entityUriString.substring(0, i).trim();
navProperty = entityUriString.substring(i + 1).trim();
} else {
entitySetName = entityUriString;
}
// now check if the entity set has primary key associated with it
i = entitySetName.indexOf('(');
if (i != -1) {
entityKey = entitySetName.substring(i + 1, entitySetName.length() - 1).trim();
entitySetName = entitySetName.substring(0, i).trim();
}
// now check if the navigation property is a link property
if (navProperty != null && navProperty.startsWith(linkPatten)) {
hasLinkProperty = true;
linkTargetProperty = navProperty.substring(linkPatten.length());
i = linkTargetProperty.indexOf('(');
if (i != -1) {
int j = linkTargetProperty.lastIndexOf(')');
if (j != -1) {
linkTargetId = linkTargetProperty.substring(i + 1, j).trim();
} else {
linkTargetId = linkTargetProperty.substring(i + 1).trim();
}
linkTargetProperty = linkTargetProperty.substring(0, i).trim();
}
}
return;
}
/**
* Helper method to parse link uri to get the link target's entity id.
* @param httpHeaders the batch request header
* @param uriInfo batch request uri info
* @param payload link request payload
* @return entity id
*/
protected OEntityId parseLinkRequestUri(HttpHeaders httpHeaders, UriInfo uriInfo, String payload) {
FormatParser<SingleLink> parser = FormatParserFactory.getParser(SingleLink.class, getResourceMediaType(), null);
SingleLink link = parser.parse(new StringReader(payload));
return OEntityIds.parse(uriInfo.getBaseUri().toString(), link.getUri());
}
}