/* * 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.client; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.OptionalDataException; import java.sql.SQLException; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.teiid.client.metadata.ParameterInfo; import org.teiid.client.plan.Annotation; import org.teiid.client.plan.PlanNode; import org.teiid.client.util.ExceptionHolder; import org.teiid.core.util.ExternalizeUtil; import org.teiid.core.util.MultiArrayOutputStream; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.jdbc.StatementImpl; import org.teiid.jdbc.TeiidDriver; import org.teiid.netty.handler.codec.serialization.CompactObjectInputStream; import org.teiid.netty.handler.codec.serialization.CompactObjectOutputStream; import org.teiid.runtime.client.TeiidClientException; /** * Results Message, used by MMStatement to get the query results. */ public class ResultsMessage implements Externalizable { static final long serialVersionUID = 3546924172976187793L; private List<? extends List<?>> results; private String[] columnNames; private String[] dataTypes; /** A description of planning that occurred as requested in the request. */ private PlanNode planDescription; /** An exception that occurred. */ private TeiidClientException exception; /** Warning could be schema validation errors or partial results warnings */ private List<Throwable> warnings; /** First row index */ private int firstRow = 0; /** Last row index */ private int lastRow; /** Final row index in complete result set, if known */ private int finalRow = -1; /** The parameters of a Stored Procedure */ private List<ParameterInfo> parameters; /** OPTION DEBUG log if OPTION DEBUG was used */ private String debugLog; private byte clientSerializationVersion; /** * Query plan annotations, if OPTION SHOWPLAN or OPTION PLANONLY was used: * Collection of Object[] where each Object[] holds annotation information * that can be used to create an Annotation implementation in JDBC. */ private Collection<Annotation> annotations; private boolean isUpdateResult; private int updateCount = -1; private boolean delayDeserialization; byte[] resultBytes; private MultiArrayOutputStream serializationBuffer; private transient ITeiidServerVersion teiidVersion; public ResultsMessage(){ } public ResultsMessage(List<? extends List<?>> results, String[] columnNames, String[] dataTypes){ this.results = results; setFirstRow( 1 ); setLastRow( results.size() ); this.columnNames = columnNames; this.dataTypes = dataTypes; } public List<? extends List<?>> getResultsList() { return results; } public void processResults() throws SQLException { if (results == null && resultBytes != null) { try { CompactObjectInputStream ois = new CompactObjectInputStream(new ByteArrayInputStream(resultBytes), ResultsMessage.class.getClassLoader()); results = BatchSerializer.getInstance(getTeiidVersion()).readBatch(ois, dataTypes); } catch (IOException e) { throw new SQLException(e); } catch (ClassNotFoundException e) { throw new SQLException(e); } finally { resultBytes = null; } } } public void setResults(List<?>[] results) { this.results = Arrays.asList(results); } public void setResults(List<? extends List<?>> results) { this.results = results; } public String[] getColumnNames() { return this.columnNames; } public String[] getDataTypes() { return this.dataTypes; } /** * @return */ public TeiidClientException getException() { return exception; } /** * @return */ public int getFinalRow() { return finalRow; } /** * @return */ public int getFirstRow() { return firstRow; } /** * @return */ public int getLastRow() { return lastRow; } /** * @return */ public PlanNode getPlanDescription() { return planDescription; } /** * @return */ public List<Throwable> getWarnings() { return warnings; } /** * @param exception */ public void setException(Throwable e) { if(e instanceof TeiidClientException) { this.exception = (TeiidClientException)e; } else { this.exception = new TeiidClientException(e, e.getMessage()); } } /** * @param i */ public void setFinalRow(int i) { finalRow = i; } /** * @param i */ public void setFirstRow(int i) { firstRow = i; } /** * @param i */ public void setLastRow(int i) { lastRow = i; } /** * @param object */ public void setPlanDescription(PlanNode object) { planDescription = object; } /** * @param list */ public void setWarnings(List<Throwable> list) { warnings = list; } /** * @return */ public List getParameters() { return parameters; } /** * @param list */ public void setParameters(List list) { parameters = list; } /** * @param strings */ public void setColumnNames(String[] columnNames) { this.columnNames = columnNames; } /** * @param strings */ public void setDataTypes(String[] dataTypes) { this.dataTypes = dataTypes; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { columnNames = ExternalizeUtil.readStringArray(in); dataTypes = ExternalizeUtil.readStringArray(in); // Row data results = BatchSerializer.getInstance(getTeiidVersion()).readBatch(in, dataTypes); // Plan Descriptions planDescription = (PlanNode)in.readObject(); ExceptionHolder holder = (ExceptionHolder)in.readObject(); if (holder != null) { this.exception = new TeiidClientException(holder.getException()); } //delayed deserialization if (results == null && this.exception == null) { int length = in.readInt(); resultBytes = new byte[length]; in.readFully(resultBytes); } List<ExceptionHolder> holderList = (List<ExceptionHolder>)in.readObject(); if (holderList != null) { this.warnings = ExceptionHolder.toThrowables(holderList); } firstRow = in.readInt(); lastRow = in.readInt(); finalRow = in.readInt(); //Parameters parameters = ExternalizeUtil.readList(in, ParameterInfo.class); debugLog = (String)in.readObject(); annotations = ExternalizeUtil.readList(in, Annotation.class); isUpdateResult = in.readBoolean(); if (isUpdateResult) { try { updateCount = in.readInt(); } catch (OptionalDataException e) { } catch (EOFException e) { } } } public void writeExternal(ObjectOutput out) throws IOException { ExternalizeUtil.writeArray(out, columnNames); ExternalizeUtil.writeArray(out, dataTypes); // Results data BatchSerializer batchSerializer = BatchSerializer.getInstance(getTeiidVersion()); if (delayDeserialization) { batchSerializer.writeBatch(out, dataTypes, null, clientSerializationVersion); } else { batchSerializer.writeBatch(out, dataTypes, results, clientSerializationVersion); } // Plan descriptions out.writeObject(this.planDescription); if (exception != null) { out.writeObject(new ExceptionHolder(exception)); } else { out.writeObject(exception); } if (delayDeserialization && results != null) { serialize(batchSerializer, true); out.writeInt(serializationBuffer.getCount()); serializationBuffer.writeTo(out); serializationBuffer = null; } if (this.warnings != null) { out.writeObject(ExceptionHolder.toExceptionHolders(this.warnings)); } else { out.writeObject(this.warnings); } out.writeInt(firstRow); out.writeInt(lastRow); out.writeInt(finalRow); // Parameters ExternalizeUtil.writeList(out, parameters); out.writeObject(debugLog); ExternalizeUtil.writeCollection(out, annotations); out.writeBoolean(isUpdateResult); if (isUpdateResult) { out.writeInt(updateCount); } } /** * Serialize the result data * @return the size of the data bytes * @throws IOException */ public int serialize(BatchSerializer batchSerializer, boolean keepSerialization) throws IOException { if (serializationBuffer == null) { serializationBuffer = new MultiArrayOutputStream(1 << 13); CompactObjectOutputStream oos = new CompactObjectOutputStream(serializationBuffer); batchSerializer.writeBatch(oos, dataTypes, results, clientSerializationVersion); oos.close(); } int result = serializationBuffer.getCount(); if (!keepSerialization) { serializationBuffer = null; } return result; } /** * @return */ public Collection<Annotation> getAnnotations() { return annotations; } /** * @return */ public String getDebugLog() { return debugLog; } /** * @param collection */ public void setAnnotations(Collection<Annotation> collection) { annotations = collection; } /** * @param string */ public void setDebugLog(String string) { debugLog = string; } /* * @see java.lang.Object#toString() */ public String toString() { return new StringBuffer("ResultsMessage rowCount=") //$NON-NLS-1$ .append(results == null ? 0 : results.size()) .append(" finalRow=") //$NON-NLS-1$ .append(finalRow) .toString(); } public void setUpdateResult(boolean isUpdateResult) { this.isUpdateResult = isUpdateResult; } public boolean isUpdateResult() { return isUpdateResult; } public byte getClientSerializationVersion() { return clientSerializationVersion; } public void setClientSerializationVersion(byte clientSerializationVersion) { this.clientSerializationVersion = clientSerializationVersion; } public void setUpdateCount(int updateCount) { this.updateCount = updateCount; } public int getUpdateCount() { return updateCount; } public void setDelayDeserialization(boolean delayDeserialization) { this.delayDeserialization = delayDeserialization; } public ITeiidServerVersion getTeiidVersion() { if (teiidVersion == null) teiidVersion = TeiidDriver.getInstance().getTeiidVersion(); return teiidVersion; } /** * Only set when this message is received from the teiid instance * and analysed by {@link StatementImpl} * * @param teiidVersion */ public void setTeiidVersion(ITeiidServerVersion teiidVersion) { this.teiidVersion = teiidVersion; } }