package org.odata4j.producer.resources; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import org.odata4j.producer.resources.ODataBatchProvider.HTTP_METHOD; /** * * Copyright 2013 Halliburton * @author <a href="mailto:peng.chen@halliburton.com">Kevin Chen</a> * */ @Provider @Consumes(ODataBatchProvider.MULTIPART_MIXED) public class ODataBatchUnitProvider implements MessageBodyReader<List<ODataBatchUnit>> { @Context HttpHeaders httpHeaders; @Context UriInfo uriInfo; final String ContentType = "content-type:"; @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] antns, MediaType mt) { if (genericType instanceof ParameterizedType) { for (Type gType : ((ParameterizedType) genericType) .getActualTypeArguments()) { if (gType == ODataBatchUnit.class && type == List.class) { return true; } } } return false; } @Override public List<ODataBatchUnit> readFrom(Class<List<ODataBatchUnit>> type, Type genericType, Annotation[] antns, MediaType mt, MultivaluedMap<String, String> mm, InputStream inputStream) throws IOException, WebApplicationException { BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); return parse(mt, br); } private List<ODataBatchUnit> parse(MediaType mt, BufferedReader br) throws IOException { String currentLine = ""; StringBuilder sb = null; List<ODataBatchUnit> parts = new ArrayList<ODataBatchUnit>(); String boundary = mt.getParameters().get("boundary"); boundary = "--" + boundary; String endBatchBoundary = boundary + "--"; while ((currentLine = br.readLine()) != null) { // check if it is start of a new boundary if (currentLine.equals(boundary)) { if (sb != null) { ODataBatchUnit part = parseOnePart(sb.toString()); if (parts != null) parts.add(part); } sb = new StringBuilder(); } else if (currentLine.equals(endBatchBoundary)) { if (sb != null) { ODataBatchUnit part = parseOnePart(sb.toString()); if (parts != null) parts.add(part); } return parts; } else { if (sb != null) { sb.append(currentLine).append("\n"); } } } if (sb != null) { ODataBatchUnit part = parseOnePart(sb.toString()); if (part != null) { parts.add(part); } } return parts; } private ODataBatchUnit parseOnePart(String content) throws IOException { boolean isHeader = true; boolean isChangeSet = false; String cType = null; BufferedReader br = new BufferedReader(new StringReader(content)); String currentLine = null; // StringBuilder sb = new StringBuilder(); ODataBatchUnit unit = null; while ((currentLine = br.readLine()) != null) { if (isHeader) { // check if this is the end of the header if (currentLine.trim().equals("")) { isHeader = false; if (isChangeSet) { unit = parseChangeSet(cType, br); br.close(); return unit; } else { unit = parseSingleUnit(br); br.close(); return unit; } } else { if (cType == null && currentLine.toLowerCase().startsWith(ContentType)) { cType = currentLine.substring(ContentType.length()).trim(); if (cType.startsWith(ODataBatchProvider.MULTIPART_MIXED)) { isChangeSet = true; } else if (cType.toLowerCase().startsWith("application/http")) { isChangeSet = false; } } } } } br.close(); return null; } private ODataBatchUnit parseSingleUnit(BufferedReader br) throws IOException { String currentLine = null; HTTP_METHOD httpMethod = null; String uri = null; boolean isHeader = true; MultivaluedMap<String, String> headers = new HeaderMap(); StringBuilder sb = new StringBuilder(); while ((currentLine = br.readLine()) != null) { if (httpMethod == null) { for (HTTP_METHOD method : HTTP_METHOD.values()) { currentLine = currentLine.trim(); if (currentLine.startsWith(method.name())) { uri = currentLine .substring(method.name().length() + 1); int lastIdx = uri.lastIndexOf(" "); if (lastIdx != -1) { uri = uri.substring(0, lastIdx).trim(); } httpMethod = method; break; } } } else { if (isHeader) { if (currentLine.isEmpty()) { isHeader = false; } else { addHeader(currentLine, headers); } } else { sb.append(currentLine); } } } // the 2 null will be provided by context injection return create(httpMethod, httpHeaders, uriInfo, uri, sb.toString(), headers); } private ODataBatchUnit parseChangeSet(String cType, BufferedReader br) throws IOException { MediaType mt = BatchRequestResource.getMediaType(cType); List<ODataBatchUnit> childList = parse(mt, br); ODataBatchChangeSetUnit changeSet = new ODataBatchChangeSetUnit(); for (ODataBatchUnit unit : childList) { changeSet.addPart((ODataBatchSingleUnit) unit); } System.out.println("change set parts: " + changeSet.getParts().size()); return changeSet; } private void addHeader(String currentLine, MultivaluedMap<String, String> headers) { Integer idx = currentLine.indexOf(':'); String key = currentLine.substring(0, idx); String value = currentLine.substring(idx + 1).trim(); headers.putSingle(key, value); } private ODataBatchUnit create(HTTP_METHOD httpMethod, HttpHeaders topHttpHeaders, UriInfo topUriInfo, String uri, String contents, MultivaluedMap<String, String> headers) { ODataBatchUnit unit = null; try { switch (httpMethod) { case POST: unit = new ODataBatchPostUnit(topHttpHeaders, topUriInfo, uri, contents, headers); break; case MERGE: unit = new ODataBatchMergeUnit(topHttpHeaders, topUriInfo, uri, contents, headers); break; case DELETE: unit = new ODataBatchDelUnit(topHttpHeaders, topUriInfo, uri, contents, headers); break; case GET: unit = new ODataBatchGetUnit(topHttpHeaders, topUriInfo, uri, headers); break; case PUT: unit = new ODataBatchPutUnit(topHttpHeaders, topUriInfo, uri, contents, headers); break; } } catch (URISyntaxException e) { unit = null; } return unit; } }