/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.olingo.service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmParameter;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ODataServerError;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriInfoResource;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.core.ServiceHandler;
import org.apache.olingo.server.core.ServiceRequest;
import org.apache.olingo.server.core.requests.ActionRequest;
import org.apache.olingo.server.core.requests.DataRequest;
import org.apache.olingo.server.core.requests.FunctionRequest;
import org.apache.olingo.server.core.requests.MediaRequest;
import org.apache.olingo.server.core.requests.MetadataRequest;
import org.apache.olingo.server.core.requests.OperationRequest;
import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
import org.apache.olingo.server.core.responses.*;
import org.teiid.common.buffer.impl.BufferManagerImpl;
import org.teiid.core.TeiidException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.types.BlobType;
import org.teiid.core.types.TransformationException;
import org.teiid.core.types.XMLType;
import org.teiid.core.util.ReaderInputStream;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.odata.api.BaseResponse;
import org.teiid.odata.api.Client;
import org.teiid.odata.api.QueryResponse;
import org.teiid.odata.api.UpdateResponse;
import org.teiid.olingo.EdmComplexResponse;
import org.teiid.olingo.ODataPlugin;
import org.teiid.olingo.service.ProcedureSQLBuilder.ActionParameterValueProvider;
import org.teiid.olingo.service.ProcedureSQLBuilder.FunctionParameterValueProvider;
import org.teiid.olingo.service.ProcedureSQLBuilder.ProcedureReturn;
import org.teiid.query.function.source.XMLSystemFunctions;
import org.teiid.query.sql.lang.Delete;
import org.teiid.query.sql.lang.Insert;
import org.teiid.query.sql.lang.Query;
import org.teiid.query.sql.lang.Update;
import org.teiid.query.sql.symbol.XMLSerialize;
public class TeiidServiceHandler implements ServiceHandler {
static class ExpandNode {
EdmNavigationProperty navigationProperty;
List<ExpandNode> children = new ArrayList<TeiidServiceHandler.ExpandNode>();
}
private static final String PREFERENCE_APPLIED = "Preference-Applied";
private static final String ODATA_MAXPAGESIZE = "odata.maxpagesize";
private boolean prepared = true;
private OData odata;
private ServiceMetadata serviceMetadata;
private String schemaName;
private UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
private static ThreadLocal<Client> CLIENT = new ThreadLocal<Client>();
public static Client getClient() {
return CLIENT.get();
}
public static void setClient(Client client) {
CLIENT.set(client);
}
public TeiidServiceHandler(String schemaName) {
this.schemaName = schemaName;
}
@Override
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
public void setPrepared(boolean flag) {
this.prepared = flag;
}
@Override
public void readMetadata(MetadataRequest request, MetadataResponse response)
throws ODataLibraryException, ODataApplicationException {
response.writeMetadata();
}
@Override
public void readServiceDocument(ServiceDocumentRequest request,
ServiceDocumentResponse response) throws ODataLibraryException,
ODataApplicationException {
response.writeServiceDocument(request.getODataRequest().getRawBaseUri());
}
class UniqueNameGenerator {
private final AtomicInteger groupCount = new AtomicInteger(0);
public String getNextGroup() {
String aliasGroup = "g"+this.groupCount.getAndIncrement(); //$NON-NLS-1$
return aliasGroup;
}
}
@Override
public <T extends ServiceResponse> void read(final DataRequest request, T response)
throws ODataLibraryException, ODataApplicationException {
final ODataSQLBuilder visitor = new ODataSQLBuilder(odata,
getClient().getMetadataStore(), this.prepared, true,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
final BaseResponse queryResponse;
try {
Query query = visitor.selectQuery();
queryResponse = executeQuery(request, request.isCountRequest(), visitor, query);
} catch (Throwable e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
response.accepts(new ServiceResponseVisior() {
public void visit(CountResponse response)
throws ODataLibraryException, ODataApplicationException {
org.teiid.odata.api.CountResponse cr = (org.teiid.odata.api.CountResponse) queryResponse;
response.writeCount(cr.getCount());
}
public void visit(PrimitiveValueResponse response)
throws ODataLibraryException, ODataApplicationException {
EntityCollection entitySet = (EntityCollection)queryResponse;
if (!entitySet.getEntities().isEmpty()) {
Entity entity = entitySet.getEntities().get(0);
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
Property property = entity.getProperty(edmProperty.getName());
if (property == null) {
response.writeNotFound(true);
}
else if (property.getValue() == null) {
response.writeNoContent(true);
} else {
response.write(property.getValue());
}
} else {
response.writeNotFound(true);
}
}
public void visit(PropertyResponse response)
throws ODataLibraryException, ODataApplicationException {
EntityCollection entitySet = (EntityCollection)queryResponse;
if (!entitySet.getEntities().isEmpty()) {
Entity entity = entitySet.getEntities().get(0);
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
Property property = entity.getProperty(edmProperty.getName());
response.writeProperty(edmProperty.getType(), property);
}
else {
response.writeNotFound(true);
}
}
public void visit(StreamResponse response)
throws ODataLibraryException, ODataApplicationException {
EntityCollectionResponse entitySet = (EntityCollectionResponse)queryResponse;
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
Object value = entitySet.getStream(edmProperty.getName());
if (value == null) {
response.writeNoContent(true);
}
else {
try {
handleLobResult(getClient().getProperty(Client.CHARSET), value, response);
} catch (SQLException e) {
LogManager.logDetail(LogConstants.CTX_ODATA, e);
response.writeServerError(true);
}
}
}
public void visit(EntityResponse response)
throws ODataLibraryException, ODataApplicationException {
EntityCollection entitySet = (EntityCollection)queryResponse;
if (entitySet.getEntities().isEmpty()) {
if (visitor.hasNavigation()) {
response.writeNoContent(true);
} else {
response.writeNotFound(true);
}
} else {
response.writeReadEntity(visitor.getContext().getEdmEntityType(),
entitySet.getEntities().get(0));
}
}
public void visit(EntitySetResponse response)
throws ODataLibraryException, ODataApplicationException {
sendResults(request, visitor, queryResponse, response);
}
});
}
private void sendResults(final DataRequest request,
final ODataSQLBuilder visitor,
final BaseResponse queryResponse, EntitySetResponse response)
throws ODataApplicationException, SerializerException {
if (request.getPreference(ODATA_MAXPAGESIZE) != null) {
response.writeHeader(PREFERENCE_APPLIED,
ODATA_MAXPAGESIZE+"="+ request.getPreference(ODATA_MAXPAGESIZE)); //$NON-NLS-1$
}
EntityCollectionResponse result = (EntityCollectionResponse)queryResponse;
if (result.getNextToken() != null) {
try {
String nextUri = request.getODataRequest().getRawBaseUri()
+request.getODataRequest().getRawODataPath()
+ "?"
+buildNextToken(request.getODataRequest().getRawQueryPath(), result.getNextToken());
result.setNext(new URI(nextUri));
} catch (URISyntaxException e) {
throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault(), e);
} catch (MalformedURLException e) {
throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault(), e);
}
}
response.writeReadEntitySet(visitor.getContext().getEdmEntityType(), result);
}
String buildNextToken(final String queryPath, String nextToken)
throws URISyntaxException, MalformedURLException {
StringBuilder sb = new StringBuilder();
if (queryPath != null) {
String[] pairs = queryPath.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
String key = pair.substring(0, idx);
if (!key.equals("$skiptoken")) {
if (sb.length()>0) {
sb.append("&");
}
sb.append(pair);
}
}
}
if (sb.length() > 0) {
sb.append("&$skiptoken=").append(nextToken);
} else {
sb.append("$skiptoken=").append(nextToken);
}
return sb.toString();
}
private void sendResults(final DataRequest request,
final ODataSQLBuilder visitor,
final BaseResponse queryResponse, EdmComplexResponse response)
throws ODataApplicationException, SerializerException {
if (request.getPreference(ODATA_MAXPAGESIZE) != null) {
response.writeHeader(PREFERENCE_APPLIED,
ODATA_MAXPAGESIZE+"="+ request.getPreference(ODATA_MAXPAGESIZE)); //$NON-NLS-1$
}
CrossJoinResult result = (CrossJoinResult)queryResponse;
URI next = null;
if (result.getNextToken() != null) {
try {
next = new URI(request.getODataRequest().getRawRequestUri()
+ (request.getODataRequest().getRawQueryPath() == null ?"?$skiptoken=":"&$skiptoken=")
+ result.getNextToken());
} catch (URISyntaxException e) {
throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault(), e);
}
}
response.writeComplexType(result, next);
}
private BaseResponse executeQuery(final ServiceRequest request, boolean countRequest,
final ODataSQLBuilder visitor, Query query) throws SQLException {
if (countRequest) {
return getClient().executeCount(query, visitor.getParameters());
}
else {
String pageSize = getPageSize(request);
QueryResponse result = new EntityCollectionResponse(request
.getODataRequest().getRawBaseUri(),
visitor.getContext());
if (visitor.getContext() instanceof CrossJoinNode) {
result = new CrossJoinResult(request.getODataRequest().getRawBaseUri(),
(CrossJoinNode) visitor.getContext());
} else if (visitor.getContext() instanceof ComplexDocumentNode) {
ComplexDocumentNode cdn = (ComplexDocumentNode)visitor.getContext();
result = new OperationResponseImpl(cdn.getProcedureReturn());
}
getClient().executeSQL(query, visitor.getParameters(),
visitor.includeTotalSize(), visitor.getSkip(),
visitor.getTop(), visitor.getNextToken(), Integer.parseInt(pageSize), result);
return result;
}
}
private String getPageSize(final ServiceRequest request) {
String pageSize = request.getPreference(ODATA_MAXPAGESIZE);
if (pageSize == null) {
if (getClient().getProperty(Client.BATCH_SIZE) == null) {
pageSize = String.valueOf(BufferManagerImpl.DEFAULT_PROCESSOR_BATCH_SIZE);
}
else {
pageSize = getClient().getProperty(Client.BATCH_SIZE);
}
}
return pageSize;
}
private void checkExpand(UriInfoResource queryInfo) {
if (queryInfo.getExpandOption() != null && !queryInfo.getExpandOption().getExpandItems().isEmpty()) {
throw new UnsupportedOperationException("Expand is not supported"); //$NON-NLS-1$
}
}
private UpdateResponse performInsert(String rawURI, UriInfo uriInfo,
EdmEntityType entityType, Entity entity) throws SQLException, TeiidException {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
rawURI, this.serviceMetadata, this.nameGenerator);
visitor.visit(uriInfo);
Insert command = visitor.insert(entityType, entity, null, this.prepared);
return getClient().executeUpdate(command, visitor.getParameters());
}
private int insertDepth(EdmEntityType entityType, Entity entity) throws SQLException, TeiidException {
int depth = 1;
int childDepth = 0;
for (String navigationName:entityType.getNavigationPropertyNames()) {
EdmNavigationProperty navProperty = entityType.getNavigationProperty(navigationName);
Link navLink = entity.getNavigationLink(navigationName);
if (navLink != null && navLink.getInlineEntity() != null) {
childDepth = Math.max(childDepth, insertDepth(navProperty.getType(), navLink.getInlineEntity()));
} else if (navLink != null && navLink.getInlineEntitySet() != null && !navLink.getInlineEntitySet().getEntities().isEmpty()) {
for (Entity inlineEntity:navLink.getInlineEntitySet().getEntities()) {
childDepth = Math.max(childDepth, insertDepth(navProperty.getType(), inlineEntity));
}
}
}
return depth + childDepth;
}
private UpdateResponse performDeepInsert(String rawURI, UriInfo uriInfo,
EdmEntityType entityType, Entity entity, List<ExpandNode> expandNodes) throws SQLException, TeiidException {
UpdateResponse response = performInsert(rawURI, uriInfo, entityType, entity);
for (String navigationName:entityType.getNavigationPropertyNames()) {
EdmNavigationProperty navProperty = entityType.getNavigationProperty(navigationName);
Link navLink = entity.getNavigationLink(navigationName);
if (navLink != null && navLink.getInlineEntity() != null) {
ExpandNode node = new ExpandNode();
node.navigationProperty = navProperty;
expandNodes.add(node);
performDeepInsert(rawURI, uriInfo, navProperty.getType(), navLink.getInlineEntity(), node.children);
} else if (navLink != null && navLink.getInlineEntitySet() != null && !navLink.getInlineEntitySet().getEntities().isEmpty()) {
ExpandNode node = new ExpandNode();
node.navigationProperty = navProperty;
expandNodes.add(node);
for (Entity inlineEntity:navLink.getInlineEntitySet().getEntities()) {
performDeepInsert(rawURI, uriInfo, navProperty.getType(), inlineEntity, node.children);
}
}
}
return response;
}
@Override
public void createEntity(DataRequest request, Entity entity,
EntityResponse response) throws ODataLibraryException,
ODataApplicationException {
EdmEntityType entityType = request.getEntitySet().getEntityType();
String txn;
try {
txn = getClient().startTransaction();
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
boolean success = false;
try {
List<ExpandNode> expands = new ArrayList<TeiidServiceHandler.ExpandNode>();
int insertDepth = insertDepth(entityType, entity);
ODataSQLBuilder.checkExpandLevel(insertDepth - 1); //don't count the root
UpdateResponse updateResponse = performDeepInsert(request
.getODataRequest().getRawBaseUri(), request.getUriInfo(),
entityType, entity, expands);
if (updateResponse != null && updateResponse.getUpdateCount() == 1) {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), true, false,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
Query query = visitor.selectWithEntityKey(entityType,
entity, updateResponse.getGeneratedKeys(), expands);
LogManager.logDetail(LogConstants.CTX_ODATA, null, "created entity = ", entityType.getName(), " with key=", query.getCriteria().toString()); //$NON-NLS-1$ //$NON-NLS-2$
EntityCollectionResponse result = new EntityCollectionResponse(
request.getODataRequest().getRawBaseUri(),
visitor.getContext());
getClient().executeSQL(query, visitor.getParameters(), false, null, null, null, 1, result);
if (!result.getEntities().isEmpty()) {
entity = result.getEntities().get(0);
String location = EntityResponse.buildLocation(request.getODataRequest().getRawBaseUri(),
entity,
request.getEntitySet().getName(),
entityType);
entity.setId(new URI(location));
}
response.writeCreatedEntity(request.getEntitySet(), entity);
}
else {
response.writeNotModified();
}
getClient().commit(txn);
success = true;
} catch (EdmPrimitiveTypeException | TeiidException | SQLException | URISyntaxException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} finally {
if (!success) {
try {
getClient().rollback(txn);
} catch (SQLException e1) {
// ignore
}
}
}
}
@Override
public void updateEntity(DataRequest request, Entity entity, boolean merge,
String entityETag, EntityResponse response)
throws ODataLibraryException, ODataApplicationException {
// TODO: need to match entityETag.
checkETag(entityETag);
UpdateResponse updateResponse = null;
if (merge) {
try {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
EdmEntityType entityType = request.getEntitySet().getEntityType();
Update update = visitor.update(entityType, entity, this.prepared);
updateResponse = getClient().executeUpdate(update, visitor.getParameters());
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} catch (TeiidException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
}
else {
// delete, then insert
String txn = startTransaction();
boolean success = false;
try {
// build insert first as it could fail to validate
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(),
this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
EdmEntityType entityType = request.getEntitySet().getEntityType();
List<UriParameter> keys = request.getKeyPredicates();
Insert command = visitor.insert(entityType, entity, keys, this.prepared);
//run delete
ODataSQLBuilder deleteVisitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(),
this.serviceMetadata, this.nameGenerator);
deleteVisitor.visit(request.getUriInfo());
Delete delete = deleteVisitor.delete();
updateResponse = getClient().executeUpdate(delete, deleteVisitor.getParameters());
//run insert
updateResponse = getClient().executeUpdate(command, visitor.getParameters());
commit(txn);
success = true;
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} catch (TeiidException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} finally {
if (!success) {
rollback(txn);
}
}
}
if (updateResponse!= null && updateResponse.getUpdateCount() > 0) {
response.writeUpdatedEntity();
}
else {
response.writeNotModified();
}
}
private void checkETag(String entityETag) throws ODataApplicationException{
if (entityETag != null && !entityETag.equals("*")) {
throw new ODataApplicationException(
ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16030),
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),
Locale.getDefault());
}
}
@Override
public void deleteEntity(DataRequest request, String entityETag,
EntityResponse response) throws ODataLibraryException, ODataApplicationException {
// TODO: need to match entityETag.
checkETag(entityETag);
UpdateResponse updateResponse = null;
try {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
Delete delete = visitor.delete();
updateResponse = getClient().executeUpdate(delete, visitor.getParameters());
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
if (updateResponse != null && updateResponse.getUpdateCount() > 0) {
response.writeDeletedEntityOrReference();
}
else {
//since DELETE is idempotent same response as otherwise success operation.
response.writeDeletedEntityOrReference();
}
}
/**
* since Teiid only deals with primitive types, merge does not apply
*/
@Override
public void updateProperty(DataRequest request, Property property, boolean rawValue,
boolean merge, String entityETag, PropertyResponse response)
throws ODataLibraryException, ODataApplicationException {
// TODO: need to match entityETag.
checkETag(entityETag);
UpdateResponse updateResponse = null;
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
try {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(),this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
Update update = visitor.updateProperty(edmProperty, property, this.prepared, rawValue);
updateResponse = getClient().executeUpdate(update, visitor.getParameters());
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} catch (TeiidException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
if (updateResponse != null && updateResponse.getUpdateCount() > 0) {
response.writePropertyUpdated();
} else {
response.writeNotModified();
}
}
@Override
public void upsertStreamProperty(DataRequest request, String entityETag,
InputStream streamContent, NoContentResponse response)
throws ODataLibraryException, ODataApplicationException {
UpdateResponse updateResponse = null;
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
try {
ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, false,
request.getODataRequest().getRawBaseUri(),this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
Update update = visitor.updateStreamProperty(edmProperty, streamContent);
updateResponse = getClient().executeUpdate(update, visitor.getParameters());
} catch (SQLException | TeiidException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
if (updateResponse != null && updateResponse.getUpdateCount() > 0) {
response.writeNoContent();
} else {
response.writeNotModified();
}
}
interface OperationParameterValueProvider {
Object getValue(EdmParameter parameter, Class<?> runtimeType) throws TeiidProcessingException;
}
@Override
public <T extends ServiceResponse> void invoke(final FunctionRequest request,
HttpMethod method, T response) throws ODataLibraryException,
ODataApplicationException {
invokeOperation(request, new FunctionParameterValueProvider(request.getParameters()), response);
}
@Override
public <T extends ServiceResponse> void invoke(final ActionRequest request,
String eTag, T response) throws ODataLibraryException, ODataApplicationException {
checkETag(eTag);
invokeOperation(request, new ActionParameterValueProvider(request.getPayload(), request), response);
}
private <T extends ServiceResponse> void invokeOperation(
final OperationRequest request,
OperationParameterValueProvider parameters, T response)
throws ODataApplicationException, ODataLibraryException {
checkExpand(request.getUriInfo().asUriInfoResource());
final ODataSQLBuilder visitor = new ODataSQLBuilder(odata,
getClient().getMetadataStore(), this.prepared, true,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
visitor.setOperationParameterValueProvider(parameters);
visitor.visit(request.getUriInfo());
final OperationResponseImpl queryResponse;
try {
if (visitor.getContext() instanceof NoDocumentNode) {
NoDocumentNode cdn = (NoDocumentNode)visitor.getContext();
ProcedureReturn procReturn = cdn.getProcedureReturn();
queryResponse = new OperationResponseImpl(procReturn);
getClient().executeCall(cdn.getQuery(), visitor.getParameters(), procReturn, queryResponse);
} else {
Query query = visitor.selectQuery();
queryResponse = (OperationResponseImpl)executeQuery(request, request.isCountRequest(), visitor, query);
}
} catch (Throwable e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
/*
try {
MetadataStore store = getClient().getMetadataStore();
ProcedureSQLBuilder builder = new ProcedureSQLBuilder(store.getSchema(schemaName), request);
ProcedureReturn procedureReturn = builder.getReturn();
result = new OperationResponseImpl(procedureReturn);
getClient().executeCall(builder.buildProcedureSQL(), builder.getSqlParameters(), procedureReturn, result);
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
} catch (TeiidException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
*/
final OperationResponseImpl operationResult = queryResponse;
response.accepts(new ServiceResponseVisior() {
@Override
public void visit(PropertyResponse response)
throws ODataLibraryException, ODataApplicationException {
Property property = (Property)operationResult.getResult();
Object value = property.getValue();
if (value instanceof SQLXML || value instanceof Blob || value instanceof Clob) {
try {
handleLobResult(getClient().getProperty(Client.CHARSET), value, response);
} catch (SQLException e) {
LogManager.logDetail(LogConstants.CTX_ODATA, e);
response.writeServerError(true);
}
}
else {
response.writeProperty(request.getReturnType().getType(), property);
}
}
});
}
@Override
public void readMediaStream(MediaRequest request, StreamResponse response)
throws ODataLibraryException, ODataApplicationException {
response.writeServerError(true);
}
@Override
public void upsertMediaStream(MediaRequest request, String entityETag,
InputStream mediaContent, NoContentResponse response)
throws ODataLibraryException, ODataApplicationException {
response.writeServerError(true);
}
@Override
public void anyUnsupported(ODataRequest request, ODataResponse response)
throws ODataLibraryException, ODataApplicationException {
throw new ODataApplicationException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16049),
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),
Locale.getDefault());
}
@Override
public void addReference(DataRequest request, String entityETag,
URI referenceId, NoContentResponse response)
throws ODataLibraryException, ODataApplicationException {
manageReference(request, referenceId, response, false);
}
@Override
public void updateReference(DataRequest request, String entityETag,
URI referenceId, NoContentResponse response)
throws ODataLibraryException, ODataApplicationException {
manageReference(request, referenceId, response, false);
}
private void manageReference(DataRequest request, URI referenceId,
NoContentResponse response, boolean delete) throws ODataApplicationException {
UpdateResponse updateResponse = null;
try {
ReferenceUpdateSQLBuilder visitor = new ReferenceUpdateSQLBuilder(getClient().getMetadataStore(),
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.odata);
visitor.visit(request.getUriInfo());
Update update = visitor.updateReference(referenceId, this.prepared, delete);
updateResponse = getClient().executeUpdate(update, visitor.getParameters());
} catch (SQLException e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
if (updateResponse != null && updateResponse.getUpdateCount() > 0) {
response.writeNoContent();
}
else {
response.writeNotModified();
}
}
@Override
public void deleteReference(DataRequest request, URI deleteId,
String entityETag, NoContentResponse response)
throws ODataLibraryException, ODataApplicationException {
manageReference(request, deleteId, response, true);
}
@Override
public String startTransaction() throws ODataLibraryException, ODataApplicationException {
try {
return getClient().startTransaction();
} catch (SQLException e) {
throw new ODataApplicationException(
ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16039),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.getDefault());
}
}
@Override
public void commit(String txnId) throws ODataLibraryException, ODataApplicationException {
try {
getClient().commit(txnId);
} catch (SQLException e) {
throw new ODataApplicationException(
ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16039),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.getDefault());
}
}
@Override
public void rollback(String txnId) throws ODataLibraryException, ODataApplicationException {
try {
getClient().rollback(txnId);
} catch (SQLException e) {
throw new ODataApplicationException(
ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16039),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.getDefault());
}
}
@Override
public void crossJoin(DataRequest request, List<String> entitySetNames,
ODataResponse response) throws ODataLibraryException,
ODataApplicationException {
final ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, true, request
.getODataRequest().getRawBaseUri(), this.serviceMetadata,
this.nameGenerator);
visitor.visit(request.getUriInfo());
try {
Query query = visitor.selectQuery();
BaseResponse queryResponse = executeQuery(request, request.isCountRequest(), visitor, query);
ContextURL.Builder builder = new ContextURL.Builder()
.asCollection()
.entitySetOrSingletonOrType("Edm.ComplexType");
EdmComplexResponse complexResponse = EdmComplexResponse.getInstance(
request, builder.build(), false, response);
sendResults(request, visitor, queryResponse, complexResponse);
} catch (Exception e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
}
private void handleLobResult(String charSet, Object result, ServiceResponse response) throws SQLException {
if (result == null) {
return; //or should this be an empty result?
}
if (result instanceof SQLXML) {
if (charSet != null) {
XMLSerialize serialize = new XMLSerialize();
serialize.setTypeString("blob"); //$NON-NLS-1$
serialize.setDeclaration(true);
serialize.setEncoding(charSet);
serialize.setDocument(true);
try {
InputStream content = ((BlobType)XMLSystemFunctions.serialize(serialize, new XMLType((SQLXML)result))).getBinaryStream();
response.writeContent(content, 200, false);
response.writeOK(ContentType.APPLICATION_OCTET_STREAM);
} catch (TransformationException e) {
throw new SQLException(e);
}
}
else {
InputStream content = ((SQLXML)result).getBinaryStream();
response.writeContent(content, 200, false);
response.writeOK(ContentType.APPLICATION_XML);
}
}
else if (result instanceof Blob) {
InputStream content = ((Blob)result).getBinaryStream();
response.writeContent(content, 200, false);
response.writeOK(ContentType.APPLICATION_OCTET_STREAM);
}
else if (result instanceof Clob) {
InputStream content = new ReaderInputStream(((Clob)result).getCharacterStream(), charSet==null?Charset.defaultCharset():Charset.forName(charSet));
response.writeContent(content, 200, false);
response.writeOK(ContentType.TEXT_PLAIN);
}
else {
InputStream content = new ByteArrayInputStream(result.toString().getBytes(charSet==null?Charset.defaultCharset():Charset.forName(charSet)));
response.writeContent(content, 200, false);
response.writeOK(ContentType.APPLICATION_OCTET_STREAM);
}
}
@Override
public void upsertEntity(DataRequest request, Entity entity, boolean merge,
String entityETag, EntityResponse response)
throws ODataLibraryException, ODataApplicationException {
final ODataSQLBuilder visitor = new ODataSQLBuilder(this.odata,
getClient().getMetadataStore(), this.prepared, true,
request.getODataRequest().getRawBaseUri(), this.serviceMetadata, this.nameGenerator);
visitor.visit(request.getUriInfo());
final EntityCollectionResponse queryResponse;
try {
Query query = visitor.selectQuery();
queryResponse = (EntityCollectionResponse)executeQuery(request, request.isCountRequest(), visitor, query);
} catch (Exception e) {
throw new ODataApplicationException(e.getMessage(),
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(),
Locale.getDefault(), e);
}
if (!queryResponse.getEntities().isEmpty()) {
updateEntity(request, entity, merge, entityETag, response);
} else {
createEntity(request, entity, response);
}
}
@Override
public boolean supportsDataIsolation() {
return false;
}
@Override
public void processError(ODataServerError error, ErrorResponse response) {
int logLevel = error.getStatusCode() >= 500?MessageLevel.ERROR:MessageLevel.WARNING;
Throwable ex = getRoot(error.getException());
//many exceptions in TeiidServiceHandler default as INTERNAL_SERVER_ERROR
//so we make a better check for codes here
if (ex instanceof TeiidNotImplementedException) {
error.setException((TeiidNotImplementedException)ex);
error.setCode(((TeiidNotImplementedException)ex).getCode());
error.setStatusCode(501);
logLevel = MessageLevel.DETAIL;
} else if (ex instanceof TeiidProcessingException) {
error.setException((TeiidProcessingException)ex);
error.setCode(((TeiidProcessingException)ex).getCode());
error.setStatusCode(400);
logLevel = MessageLevel.WARNING;
} else if (ex instanceof TeiidException) {
error.setException((TeiidException)ex);
error.setCode(((TeiidException)ex).getCode());
error.setStatusCode(500);
logLevel = MessageLevel.ERROR;
} else if (ex instanceof TeiidRuntimeException) {
error.setException((TeiidRuntimeException)ex);
error.setCode(((TeiidRuntimeException)ex).getCode());
error.setStatusCode(500);
logLevel = MessageLevel.ERROR;
}
if (ex != error.getException() && ex.getMessage() != null) {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_ODATA, MessageLevel.DETAIL) || logLevel <= MessageLevel.ERROR) {
LogManager.log(logLevel, LogConstants.CTX_ODATA, error.getException(), ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16050, error.getMessage(), ex.getMessage()));
} else {
LogManager.log(logLevel, LogConstants.CTX_DQP, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16051, error.getMessage(), ex.getMessage()));
}
} else {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_ODATA, MessageLevel.DETAIL) || logLevel <= MessageLevel.ERROR) {
LogManager.log(logLevel, LogConstants.CTX_ODATA, error.getException(), ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16052, error.getMessage()));
} else {
LogManager.log(logLevel, LogConstants.CTX_DQP, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16053, error.getMessage()));
}
}
response.writeError(error);
}
private Throwable getRoot(Throwable t) {
if (t.getCause() != null && t.getCause() != t) {
if (t.getCause() instanceof TeiidException || t.getCause() instanceof TeiidRuntimeException) {
return t.getCause();
}
return getRoot(t.getCause());
}
return t;
}
}