IDSerialGenerator_numeric.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.utils.id.serial;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.openspcoop2.utils.TipiDatabase;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.jdbc.JDBCUtilities;
import org.openspcoop2.utils.sql.ISQLQueryObject;
import org.openspcoop2.utils.sql.SQLObjectFactory;
import org.slf4j.Logger;
/**
* IDSerialGenerator_numeric
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class IDSerialGenerator_numeric {
private static SecureRandom _rnd = null;
private static synchronized void initRandom() {
if(_rnd==null) {
_rnd = new SecureRandom();
}
}
private static java.util.Random getRandom() {
if(_rnd==null) {
initRandom();
}
return _rnd;
}
public static String generate(Connection conDB,TipiDatabase tipoDatabase,
IDSerialGeneratorParameter param,Logger log, InfoStatistics infoStatistics) throws UtilsException{
long attesaAttivaJDBC = param.getSerializableTimeWaitMs();
int checkIntervalloJDBC = param.getSerializableNextIntervalTimeMs();
String protocollo = param.getProtocollo();
long counterTmp = -1;
boolean idBuildOK = false;
long scadenzaWhile = DateManager.getTimeMillis()
+ attesaAttivaJDBC;
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(out);
int iteration = 0;
List<String> messageException = new ArrayList<>();
String table = param.getTableName();
if(table==null){
if(param.getInformazioneAssociataAlProgressivo()!=null){
table = Constants.TABELLA_ID_RELATIVO_AS_LONG;
}
else{
table = Constants.TABELLA_ID_AS_LONG;
}
}
String columnInfoAssociata = param.getColumnRelativeInfo();
if(columnInfoAssociata==null){
columnInfoAssociata = Constants.TABELLA_ID_COLONNA_INFO_ASSOCIATA;
}
String columnPrg = param.getColumnPrg();
if(columnPrg==null){
columnPrg = Constants.TABELLA_ID_COLONNA_COUNTER;
}
String columnProtocollo = param.getColumnProtocol();
if(columnProtocollo==null){
columnProtocollo = Constants.TABELLA_ID_COLONNA_PROTOCOLLO;
}
// String columnCondition = "";
// String columnValueCondition = "";
// String condition = "";
// if(param.getInformazioneAssociataAlProgressivo()!=null){
// condition = " AND "+columnInfoAssociata+"=?";
// columnCondition = ","+columnInfoAssociata;
// columnValueCondition = ",?";
// }
boolean maxValueAndWrapDisabled = false;
List<String> valuesGenerated = new ArrayList<>();
boolean rowNotExistsAndSerializableLevelNotFound = false;
while(maxValueAndWrapDisabled==false && rowNotExistsAndSerializableLevelNotFound==false && idBuildOK==false &&
DateManager.getTimeMillis() < scadenzaWhile){
valuesGenerated = new ArrayList<>();
iteration++;
// Prima provo ad utilizzare il buffer (può darsi che un altro thread l'abbia riempito)
if(param.getSizeBuffer()>1){
String valueFromBuffer = IDSerialGeneratorBuffer.nextValue(IDSerialGenerator_numeric.class,param.getInformazioneAssociataAlProgressivo());
if(valueFromBuffer!=null){
//System.out.println("GET ["+valueFromBuffer+"] FROM BUFFER");
return valueFromBuffer;
}
}
//log.info("ancoraImbustamento interval["+checkInterval+"] secondi "+(scadenzaWhile-DateManager.getTimeMillis())/1000);
counterTmp = -1;
PreparedStatement pstmt = null;
PreparedStatement pstmtInsert = null;
ResultSet rs = null;
try{
// Lettura attuale valore
ISQLQueryObject sqlGet = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
sqlGet.addSelectField(columnPrg);
sqlGet.addFromTable(table);
sqlGet.setANDLogicOperator(true);
sqlGet.addWhereCondition(columnProtocollo+"=?");
if(param.getInformazioneAssociataAlProgressivo()!=null){
sqlGet.addWhereCondition(columnInfoAssociata+"=?");
}
sqlGet.setSelectForUpdate(true);
StringBuilder query = new StringBuilder();
// query.append("SELECT "+columnPrg+" FROM ");
// query.append(table);
// query.append(" WHERE "+columnProtocollo+"=?");
// query.append(condition);
// query.append(" FOR UPDATE");
query.append(sqlGet.createSQLQuery());
//System.out.println("SELECT ["+query.toString()+"]");
pstmt = conDB.prepareStatement(query.toString());
pstmt.setString(1, protocollo);
if(param.getInformazioneAssociataAlProgressivo()!=null){
pstmt.setString(2, param.getInformazioneAssociataAlProgressivo());
}
rs = pstmt.executeQuery();
if(rs == null) {
pstmt.close();
log.error("Creazione serial non riuscita: ResultSet is null?");
throw new UtilsException("Creazione serial non riuscita: ResultSet is null?");
}
boolean exist = rs.next();
if(!exist) {
if(JDBCUtilities.isTransactionIsolationSerializable(conDB.getTransactionIsolation(), tipoDatabase)==false) {
rowNotExistsAndSerializableLevelNotFound = true;
continue;
}
}
// incremento se esiste
if(exist){
counterTmp = rs.getLong(columnPrg);
for (int i = 0; i < param.getSizeBuffer(); i++) {
if ((counterTmp + 1) > param.getMaxValue()) {
if(param.isWrap()){
counterTmp = 0;
}else{
if(valuesGenerated.size()<=0){
maxValueAndWrapDisabled = true;
throw new Exception("Max Value of identifier has been reached");
}
else{
break; // utilizzo gli identificativi che ho generato fino ad ora.
}
}
}
counterTmp++;
valuesGenerated.add(counterTmp+"");
}
}
rs.close();
pstmt.close();
if(exist==false){
counterTmp = 1;
// CREO PRIMO COUNT!
StringBuilder queryInsert = new StringBuilder();
ISQLQueryObject sqlInsert = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
sqlInsert.addInsertTable(table);
sqlInsert.addInsertField(columnPrg, "?");
sqlInsert.addInsertField(columnProtocollo, "?");
if(param.getInformazioneAssociataAlProgressivo()!=null){
sqlInsert.addInsertField(columnInfoAssociata, "?");
}
// queryInsert.append("INSERT INTO "+table+" ("+columnPrg+","+columnProtocollo+columnCondition+") ");
// queryInsert.append(" VALUES ( ? , ? "+columnValueCondition+")");
queryInsert.append(sqlInsert.createSQLInsert());
//System.out.println("INSERT ["+queryInsert.toString()+"]");
pstmtInsert = conDB.prepareStatement(queryInsert
.toString());
pstmtInsert.setLong(1, 1);
pstmtInsert.setString(2, protocollo);
if(param.getInformazioneAssociataAlProgressivo()!=null){
pstmtInsert.setString(3, param.getInformazioneAssociataAlProgressivo());
}
pstmtInsert.execute();
pstmtInsert.close();
valuesGenerated.add(counterTmp+"");
}else{
// Incremento!
StringBuilder queryUpdate = new StringBuilder();
ISQLQueryObject sqlUpdate = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
sqlUpdate.addUpdateTable(table);
sqlUpdate.addUpdateField(columnPrg, "?");
sqlUpdate.setANDLogicOperator(true);
sqlUpdate.addWhereCondition(columnProtocollo+"=?");
if(param.getInformazioneAssociataAlProgressivo()!=null){
sqlUpdate.addWhereCondition(columnInfoAssociata+"=?");
}
// queryUpdate.append("UPDATE ");
// queryUpdate.append(table);
// queryUpdate.append(" SET "+columnPrg+" = ? WHERE "+columnProtocollo+"=?"+condition);
queryUpdate.append(sqlUpdate.createSQLUpdate());
//System.out.println("UPDATE ["+queryUpdate.toString()+"]");
pstmtInsert = conDB.prepareStatement(queryUpdate
.toString());
pstmtInsert.setLong(1, counterTmp);
pstmtInsert.setString(2, protocollo);
if(param.getInformazioneAssociataAlProgressivo()!=null){
pstmtInsert.setString(3, param.getInformazioneAssociataAlProgressivo());
}
pstmtInsert.execute();
pstmtInsert.close();
}
// Chiusura Transazione
conDB.commit();
// ID Costruito
idBuildOK = true;
} catch(Throwable e) {
ps.append("********* Exception Iteration ["+iteration+"] **********\n");
String msg = e.getMessage(); // per evitare out of memory
if(msg==null){
msg = "NULL-MESSAGE";
}
if(messageException.contains(msg)){
ps.append("Message already occurs: "+msg);
}
else{
e.printStackTrace(ps);
messageException.add(msg);
}
ps.append("\n\n");
if(infoStatistics!=null){
infoStatistics.addErrorSerializableAccess(e);
}
//System.out.println("ERRORE: "+e.getMessage());
//log.info("ERROR GET SERIAL SQL ["+e.getMessage()+"]");
try{
if( rs != null )
rs.close();
} catch(Exception er) {}
try{
if( pstmt != null )
pstmt.close();
} catch(Exception er) {}
try{
if( pstmtInsert != null )
pstmtInsert.close();
} catch(Exception er) {}
try{
conDB.rollback();
} catch(Exception er) {}
}
if(idBuildOK == false){
// Per aiutare ad evitare conflitti
try{
int intervalloDestro = checkIntervalloJDBC;
if(param.isSerializableNextIntervalTimeMsIncrementMode()){
intervalloDestro = intervalloDestro + (iteration*param.getSerializableNextIntervalTimeMsIncrement());
if(intervalloDestro>param.getMaxSerializableNextIntervalTimeMs()){
intervalloDestro = param.getMaxSerializableNextIntervalTimeMs();
}
}
int sleep = getRandom().nextInt(intervalloDestro);
//System.out.println("Sleep: "+sleep);
Utilities.sleep(sleep); // random
}catch(Exception eRandom){}
}
}
try{
if( ps != null ){
ps.flush();
}
} catch(Exception er) {}
try{
if( out != null ){
out.flush();
}
} catch(Exception er) {}
try{
if( ps != null ){
ps.close();
}
} catch(Exception er) {}
try{
if( out != null ){
out.close();
}
} catch(Exception er) {}
if(maxValueAndWrapDisabled){
String msgError = "Max Value ["+param.getMaxValue()+"] of identifier has been reached";
log.error(msgError+": "+out.toString()); // in out è presente l'intero stackTrace
throw new UtilsException(msgError);
}
if(rowNotExistsAndSerializableLevelNotFound){
String msgError = "Raw not exists and serializable level is disabled";
log.error(msgError); // in out è presente l'intero stackTrace
throw new UtilsException(msgError);
}
if(idBuildOK==false || counterTmp<=0){
String msgError = "Creazione serial non riuscita: l'accesso serializable non ha permesso la creazione del numero sequenziale";
log.error(msgError+": "+out.toString()); // in out è presente l'intero stackTrace
throw new UtilsException(msgError);
}
String vRet = valuesGenerated.remove(0);
if(valuesGenerated.size()>0){
IDSerialGeneratorBuffer.putAll(valuesGenerated,IDSerialGenerator_numeric.class,param.getInformazioneAssociataAlProgressivo());
}
//System.out.println("GET ["+vRet+"] AND SET BUFFER AT SIZE ["+valuesGenerated.size()+"]");
return vRet;
}
}