AbstractBaseOpenSPCoop2MessageDynamicContent.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.message;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.output.CountingOutputStream;
import org.openspcoop2.message.constants.Costanti;
import org.openspcoop2.message.exception.MessageException;
import org.openspcoop2.message.exception.MessageNotSupportedException;
import org.openspcoop2.message.soap.reader.OpenSPCoop2MessageSoapStreamReader;
import org.openspcoop2.utils.CopyStream;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.io.DumpByteArrayOutputStream;
import org.openspcoop2.utils.resources.Charset;
import org.openspcoop2.utils.transport.http.ContentTypeUtilities;
/**
* AbstractBaseOpenSPCoop2RestMessage
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public abstract class AbstractBaseOpenSPCoop2MessageDynamicContent<T> extends AbstractBaseOpenSPCoop2Message {
protected CountingInputStream _countingInputStream;
protected String contentType;
protected String contentTypeCharsetName = Charset.UTF_8.getValue();
protected boolean supportReadOnly = true;
protected boolean contentUpdatable = false;
protected T content;
protected boolean hasContent = false;
protected OpenSPCoop2MessageSoapStreamReader soapStreamReader;
protected DumpByteArrayOutputStream contentBuffer;
private static int soglia;
private static File repositoryFile;
public static void setSoglia(int soglia) {
AbstractBaseOpenSPCoop2MessageDynamicContent.soglia = soglia;
}
public static void setRepositoryFile(File repositoryFile) {
AbstractBaseOpenSPCoop2MessageDynamicContent.repositoryFile = repositoryFile;
}
private static boolean soapReader = false; // attivato sul gateway tramite il metodo sottostante; in modo che le
// altre applicazioni non utilizzino questa funzionalità
// (TestService,Console...)
private static int soapReaderBufferThresholdKb = 10;
public static void setSoapReader(boolean soapReader) {
AbstractBaseOpenSPCoop2MessageDynamicContent.soapReader = soapReader;
}
public static void setSoapReaderBufferThresholdKb(int bufferThresholdKb) {
AbstractBaseOpenSPCoop2MessageDynamicContent.soapReaderBufferThresholdKb = bufferThresholdKb;
}
/* Costruttore */
protected AbstractBaseOpenSPCoop2MessageDynamicContent(OpenSPCoop2MessageFactory messageFactory) {
super(messageFactory);
this.hasContent = false;
}
protected AbstractBaseOpenSPCoop2MessageDynamicContent(OpenSPCoop2MessageFactory messageFactory,
InputStream isParam, String contentType, boolean soap, OpenSPCoop2MessageSoapStreamReader soapStreamReader)
throws MessageException {
super(messageFactory);
try {
this.contentType = contentType;
if (contentType != null) {
String ch = ContentTypeUtilities.readCharsetFromContentType(contentType);
if (ch != null) {
this.contentTypeCharsetName = ch;
}
}
if (isParam != null) {
if (soap && soapStreamReader != null) {
soapStreamReader.checkException(); // senno si entra nell'if dello stream vuoto prima
}
// check se esiste del contenuto nello stream, lo stream può essere diverso da
// null però vuoto.
InputStream normalizedIs = Utilities.normalizeStream(isParam, false);
if (normalizedIs == null) {
// stream vuoto
this.hasContent = false;
} else {
if (soap) {
if (soapStreamReader == null) {
if (AbstractBaseOpenSPCoop2MessageDynamicContent.soapReader) {
this.soapStreamReader = new OpenSPCoop2MessageSoapStreamReader(messageFactory,
this.contentType, normalizedIs,
AbstractBaseOpenSPCoop2MessageDynamicContent.soapReaderBufferThresholdKb);
try {
this.soapStreamReader.read();
} finally {
// anche in caso di eccezione devo cmq aggiornare is
normalizedIs = this.soapStreamReader.getBufferedInputStream();
}
}
} else {
this.soapStreamReader = soapStreamReader;
if (this.soapStreamReader != null) {
this.soapStreamReader.checkException();
}
}
}
this._countingInputStream = new CountingInputStream(normalizedIs);
this.hasContent = true;
}
}
} catch (Exception e) {
throw new MessageException(e.getMessage(), e);
}
}
/* Metodi richiesti da chi implementa questa classe base */
protected abstract T buildContent() throws MessageException;
protected abstract T buildContent(DumpByteArrayOutputStream contentBuffer) throws MessageException;
protected abstract String buildContentAsString() throws MessageException;
protected abstract byte[] buildContentAsByteArray() throws MessageException;
protected abstract void serializeContent(OutputStream os, boolean consume) throws MessageException;
protected void setUpdatableContent() throws MessageException {
}
/* Informazioni SOAP (senza costruire il DOM) */
public OpenSPCoop2MessageSoapStreamReader getSoapReader() {
return this.soapStreamReader;
}
public void releaseSoapReader() {
if (this.soapStreamReader != null) {
this.soapStreamReader.releaseBufferedInputStream();
this.soapStreamReader.clearHeader();
this.soapStreamReader = null;
}
}
/* Input Stream con costruzione del buffer incrementale */
public boolean setInputStreamLazyBuffer(String idTransazione) throws MessageException{
if(this._countingInputStream==null) {
return false;
}
if(this.content != null || this.contentBuffer!=null) {
return false;
}
if(! (this._countingInputStream instanceof OpenSPCoop2InputStreamDynamicContent) ) {
DumpByteArrayOutputStream contentBuffer = new DumpByteArrayOutputStream(
AbstractBaseOpenSPCoop2MessageDynamicContent.soglia,
AbstractBaseOpenSPCoop2MessageDynamicContent.repositoryFile, idTransazione,
this.getMessageRole().name());
this._countingInputStream = new OpenSPCoop2InputStreamDynamicContent(this._countingInputStream, contentBuffer);
}
return true; // se è già OpenSPCoop2InputStreamDynamicContent torno true
}
/* Contenuto */
private synchronized void initializeContent(boolean readOnly, String idTransazione) throws MessageException {
if (this.hasContent) {
if (this.content == null) {
if (readOnly && this.supportReadOnly) {
if(this._countingInputStream instanceof OpenSPCoop2InputStreamDynamicContent) {
// se e' un OpenSPCoop2InputStreamDynamicContent dovrebbe gia' essere stato inizializzato il buffer
this._countingInputStream = ((OpenSPCoop2InputStreamDynamicContent)this._countingInputStream).getWrappedInputStream();
this.contentBuffer = ((OpenSPCoop2InputStreamDynamicContent)this._countingInputStream).getBuffer();
}
else {
this.contentBuffer = new DumpByteArrayOutputStream(
AbstractBaseOpenSPCoop2MessageDynamicContent.soglia,
AbstractBaseOpenSPCoop2MessageDynamicContent.repositoryFile, idTransazione,
this.getMessageRole().name());
}
try {
CopyStream.copy(this._countingInputStream, this.contentBuffer); // se e' un OpenSPCoop2InputStreamDynamicContent riverso quello che rimane nel buffer
this.content = this.buildContent(this.contentBuffer);
} catch (Throwable t) {
MessageUtils.registerParseException(this, t, true);
throw new MessageException(t.getMessage(), t);
} finally {
try {
this._countingInputStream.close();
} catch (Exception eClose) {
}
}
} else {
try {
this.content = this.buildContent();
} catch (Throwable t) {
MessageUtils.registerParseException(this, t, true);
throw new MessageException(t.getMessage(), t);
}
}
}
}
}
@Override
public boolean isContentBuilded() {
return this.content != null;
}
public InputStream getInputStreamFromContentBuffer() throws MessageException {
if(this.contentBuffer==null) {
return null;
}
try {
if(this.contentBuffer.isSerializedOnFileSystem()) {
return new FileInputStream(this.contentBuffer.getSerializedFile());
}
else {
return new ByteArrayInputStream(this.contentBuffer.toByteArray());
}
}catch(Exception e) {
throw new MessageException(e.getMessage(),e);
}
}
public InputStream getInputStream() {
return this._countingInputStream;
}
protected InputStream _getInputStream() throws MessageException {
if(this._countingInputStream instanceof OpenSPCoop2InputStreamDynamicContent) {
OpenSPCoop2InputStreamDynamicContent di = ((OpenSPCoop2InputStreamDynamicContent)this._countingInputStream);
DumpByteArrayOutputStream bf = di.getBuffer();
if(bf!=null && bf.size()>0) {
this._countingInputStream = di.getWrappedInputStream();
InputStream isBuffered = bf.getInputStream();
try {
InputStream isRemained = Utilities.normalizeStream(this._countingInputStream, false);
if(di.isInputStreamConsumed() || (isRemained==null) ) {
return isBuffered;
}
else {
return new SequenceInputStream(isBuffered,isRemained);
}
}catch(Exception e) {
throw new MessageException(e.getMessage(),e);
}
}
else {
return this._countingInputStream;
}
}
else {
return this._countingInputStream;
}
}
public boolean hasContent() throws MessageException, MessageNotSupportedException {
return this.hasContent;
}
public void initContent() throws MessageException,MessageNotSupportedException{
getContent();
}
public void initContent(boolean readOnly, String idTransazione) throws MessageException,MessageNotSupportedException{
getContent(readOnly, idTransazione);
}
public T getContent() throws MessageException, MessageNotSupportedException {
return getContent(false, null);
}
public T getContent(boolean readOnly, String idTransazione) throws MessageException, MessageNotSupportedException {
if (this.hasContent) {
if (!readOnly) {
boolean aggiornaContenuto = false;
if (this.content != null && !this.contentUpdatable) {
aggiornaContenuto = true;
}
this.contentUpdatable = true;
if (aggiornaContenuto) {
// contenuto precedentemente già creato in modalità read-only
// il metodo ha bisogno che contentUpdatable sia a true
setUpdatableContent();
}
}
// nota l'assegnazione di contentUpdatable viene usata poi dentro
// l'inizializzaizone del contenuto per rilasciare le risorse
if (this.content == null) {
this.initializeContent(readOnly, idTransazione);
}
}
return this.content; // può tornare null
}
public String getContentAsString() throws MessageException, MessageNotSupportedException {
return getContentAsString(false, null);
}
public String getContentAsString(boolean readOnly, String idTransazione)
throws MessageException, MessageNotSupportedException {
try {
if (this.hasContent) {
if (this.content == null) {
this.initializeContent(readOnly, idTransazione);
}
if (!readOnly) {
this.contentUpdatable = true;
}
return this.buildContentAsString();
}
return null;
} catch (MessageException e) {
throw e;
} catch (Exception e) {
throw new MessageException(e.getMessage(), e);
}
}
public byte[] getContentAsByteArray() throws MessageException, MessageNotSupportedException {
return getContentAsByteArray(false, null);
}
public byte[] getContentAsByteArray(boolean readOnly, String idTransazione)
throws MessageException, MessageNotSupportedException {
try {
if (this.hasContent) {
if (this.content == null) {
this.initializeContent(readOnly, idTransazione);
}
if (!readOnly) {
this.contentUpdatable = true;
}
return this.buildContentAsByteArray();
}
return null;
} catch (MessageException e) {
throw e;
} catch (Exception e) {
throw new MessageException(e.getMessage(), e);
}
}
public void updateContent(T content) throws MessageException, MessageNotSupportedException {
this.content = content;
this.contentUpdatable = true;
if (this.contentBuffer != null) {
this.contentBuffer.clearResources();
this.contentBuffer = null;
}
if (this.content != null) {
this.hasContent = true;
} else {
this.hasContent = false;
this.contentType = null;
}
}
public void setContentUpdatable() {
this.contentUpdatable = true;
}
/* ContentType */
@Override
public void updateContentType() throws MessageException {
// nop;
}
@Override
public void setContentType(String type) {
this.contentType = type;
}
@Override
public String getContentType() {
return this.contentType;
}
/* WriteTo e Save */
@Override
public void writeTo(OutputStream os, boolean consume) throws MessageException {
this.writeTo(os, consume, false, null);
}
public void writeTo(OutputStream os, boolean consume, boolean readOnly, String idTransazione)
throws MessageException {
writeTo(os, consume, readOnly, idTransazione, null);
}
public void writeTo(OutputStream os, boolean consume, boolean readOnly, String idTransazione, StringBuilder debug)
throws MessageException {
try {
if (this.hasContent) {
if (!consume && this.content == null) {
if (!readOnly) {
this.contentUpdatable = true; // riverso soap header eventuale nel content che verrà costruito
}
this.initializeContent(readOnly, idTransazione); // per poi entrare nel ramo sotto serializeContent
}
CountingOutputStream cos = new CountingOutputStream(os);
if (this.contentBuffer != null && !this.contentUpdatable) {
if (this.soapStreamReader != null && this.soapStreamReader.isSoapHeaderModified()
&& this.contentType != null) {
if (debug != null) {
debug.append(Costanti.WRITE_MODE_SERIALIZE_BUFFER_WITH_HEADER);
}
this.soapStreamReader.writeOptimizedHeaderTo(this.contentBuffer.getInputStream(), cos, true);
} else {
if (debug != null) {
debug.append(Costanti.WRITE_MODE_SERIALIZE_BUFFER);
}
this.contentBuffer.writeTo(cos);
}
} else if (this.content != null) {
if (debug != null) {
debug.append(Costanti.WRITE_MODE_SERIALIZE_CONTENT);
}
this.serializeContent(cos, consume);
} else {
if (this.soapStreamReader != null && this.soapStreamReader.isSoapHeaderModified()) {
if (debug != null) {
debug.append(Costanti.WRITE_MODE_SERIALIZE_STREAM_WITH_HEADER);
}
this.soapStreamReader.writeOptimizedHeaderTo(this._getInputStream(), cos, true);
} else {
if (debug != null) {
debug.append(Costanti.WRITE_MODE_SERIALIZE_STREAM);
}
Utilities.copy(this._getInputStream(), cos);
this._getInputStream().close();
}
}
this.outgoingsize = cos.getByteCount();
}
} catch (MessageException e) {
throw e;
} catch (Exception e) {
throw new MessageException(e.getMessage(), e);
} finally {
if (consume) {
try {
if (this.contentBuffer != null) {
this.contentBuffer.unlock();
this.contentBuffer.clearResources();
this.contentBuffer = null;
}
} catch (Throwable t) {
}
}
}
}
@Override
public void saveChanges() throws MessageException {
// nop;
}
@Override
public boolean saveRequired() {
return false;
}
/* Content Length */
@Override
public long getIncomingMessageContentLength() {
if (this._countingInputStream != null) {
return this._countingInputStream.getByteCount();
} else {
return super.getIncomingMessageContentLength();
}
}
}