IDSerialGenerator_alphanumeric.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */
  20. package org.openspcoop2.utils.id.serial;

  21. import java.io.ByteArrayOutputStream;
  22. import java.io.PrintStream;
  23. import java.security.SecureRandom;
  24. import java.sql.Connection;
  25. import java.sql.PreparedStatement;
  26. import java.sql.ResultSet;
  27. import java.util.ArrayList;
  28. import java.util.List;

  29. import org.openspcoop2.utils.TipiDatabase;
  30. import org.openspcoop2.utils.Utilities;
  31. import org.openspcoop2.utils.UtilsException;
  32. import org.openspcoop2.utils.date.DateManager;
  33. import org.openspcoop2.utils.id.ApacheGeneratorConfiguration;
  34. import org.openspcoop2.utils.id.ApacheIdentifierGenerator;
  35. import org.openspcoop2.utils.id.apache.serial.EnumTypeGenerator;
  36. import org.openspcoop2.utils.id.apache.serial.MaxReachedException;
  37. import org.openspcoop2.utils.jdbc.JDBCUtilities;
  38. import org.openspcoop2.utils.sql.ISQLQueryObject;
  39. import org.openspcoop2.utils.sql.SQLObjectFactory;
  40. import org.slf4j.Logger;

  41. /**
  42.  * IDSerialGenerator_alphanumeric
  43.  *
  44.  * @author Andrea Poli (apoli@link.it)
  45.  * @author $Author$
  46.  * @version $Rev$, $Date$
  47.  */
  48. public class IDSerialGenerator_alphanumeric {
  49.    
  50.     private static SecureRandom _rnd = null;
  51.     private static synchronized void initRandom() {
  52.         if(_rnd==null) {
  53.             _rnd = new SecureRandom();
  54.         }
  55.     }
  56.     private static java.util.Random getRandom() {
  57.         if(_rnd==null) {
  58.             initRandom();
  59.         }
  60.         return _rnd;
  61.     }

  62.    
  63.     public static String generate(Connection conDB,TipiDatabase tipoDatabase,
  64.             IDSerialGeneratorParameter param,Logger log,InfoStatistics infoStatistics) throws UtilsException{
  65.        
  66.         ApacheGeneratorConfiguration generatorConfig = new ApacheGeneratorConfiguration();
  67.         generatorConfig.setEnableLowerCaseLetter(true);
  68.         generatorConfig.setEnableUpperCaseLetter(true);
  69.         generatorConfig.setWrap(param.isWrap());
  70.         generatorConfig.setType(EnumTypeGenerator.ALPHANUMERIC);
  71.        
  72.        
  73.         long attesaAttivaJDBC = param.getSerializableTimeWaitMs();
  74.         int checkIntervalloJDBC = param.getSerializableNextIntervalTimeMs();
  75.         String protocollo = param.getProtocollo();
  76.        
  77.         String progressivoTmp = null;
  78.        
  79.         boolean idBuildOK = false;

  80.         long scadenzaWhile = DateManager.getTimeMillis()
  81.                 + attesaAttivaJDBC;

  82.         ByteArrayOutputStream out = new ByteArrayOutputStream();
  83.         PrintStream ps = new PrintStream(out);
  84.        
  85.         int iteration = 0;
  86.        
  87.         List<String> messageException = new ArrayList<>();
  88.        
  89.         String table = param.getTableName();
  90.         if(table==null){
  91.             if(param.getInformazioneAssociataAlProgressivo()!=null){
  92.                 table = Constants.TABELLA_ID_RELATIVO_AS_STRING;
  93.             }
  94.             else{
  95.                 table = Constants.TABELLA_ID_AS_STRING;
  96.             }
  97.         }
  98.        
  99.         String columnInfoAssociata = param.getColumnRelativeInfo();
  100.         if(columnInfoAssociata==null){
  101.             columnInfoAssociata = Constants.TABELLA_ID_COLONNA_INFO_ASSOCIATA;
  102.         }
  103.        
  104.         String columnPrg = param.getColumnPrg();
  105.         if(columnPrg==null){
  106.             columnPrg = Constants.TABELLA_ID_COLONNA_PROGRESSIVO;
  107.         }
  108.        
  109.         String columnProtocollo = param.getColumnProtocol();
  110.         if(columnProtocollo==null){
  111.             columnProtocollo = Constants.TABELLA_ID_COLONNA_PROTOCOLLO;
  112.         }
  113.        
  114.        
  115. //      String columnCondition = "";
  116. //      String columnValueCondition = "";
  117. //      String condition = "";
  118. //      if(param.getInformazioneAssociataAlProgressivo()!=null){
  119. //          condition = " AND "+columnInfoAssociata+"=?";
  120. //          columnCondition = ","+columnInfoAssociata;
  121. //          columnValueCondition = ",?";
  122. //      }
  123.        
  124.         boolean maxValueAndWrapDisabled = false;
  125.        
  126.         List<String> valuesGenerated = new ArrayList<>();
  127.        
  128.         boolean rowNotExistsAndSerializableLevelNotFound = false;
  129.        
  130.         while(maxValueAndWrapDisabled==false && rowNotExistsAndSerializableLevelNotFound==false && idBuildOK==false &&
  131.                 DateManager.getTimeMillis() < scadenzaWhile){

  132.             valuesGenerated = new ArrayList<>();
  133.            
  134.             iteration++;
  135.            
  136.             // Prima provo ad utilizzare il buffer (può darsi che un altro thread l'abbia riempito)
  137.             if(param.getSizeBuffer()>1){
  138.                 String valueFromBuffer = IDSerialGeneratorBuffer.nextValue(IDSerialGenerator_alphanumeric.class,param.getInformazioneAssociataAlProgressivo());
  139.                 if(valueFromBuffer!=null){
  140.                     //System.out.println("GET ["+valueFromBuffer+"] FROM BUFFER");
  141.                     return valueFromBuffer;
  142.                 }
  143.             }
  144.            
  145.             //log.info("ancoraImbustamento interval["+checkInterval+"]   secondi "+(scadenzaWhile-DateManager.getTimeMillis())/1000);

  146.             progressivoTmp = null;
  147.             PreparedStatement pstmt = null;
  148.             PreparedStatement pstmtInsert = null;
  149.             ResultSet rs = null;
  150.             try{
  151.                 // Lettura attuale valore
  152.                 ISQLQueryObject sqlGet = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
  153.                 sqlGet.addSelectField(columnPrg);
  154.                 sqlGet.addFromTable(table);
  155.                 sqlGet.setANDLogicOperator(true);
  156.                 sqlGet.addWhereCondition(columnProtocollo+"=?");
  157.                 if(param.getInformazioneAssociataAlProgressivo()!=null){
  158.                     sqlGet.addWhereCondition(columnInfoAssociata+"=?");
  159.                 }
  160.                 sqlGet.setSelectForUpdate(true);
  161.                
  162.                 StringBuilder query = new StringBuilder();
  163. //              query.append("SELECT "+columnPrg+" FROM ");
  164. //              query.append(table);
  165. //              query.append(" WHERE "+columnProtocollo+"=?");
  166. //              query.append(condition);
  167. //              query.append(" FOR UPDATE");
  168.                 query.append(sqlGet.createSQLQuery());
  169.                 //System.out.println("SELECT ["+query.toString()+"]");
  170.                 pstmt = conDB.prepareStatement(query.toString());
  171.                 pstmt.setString(1, protocollo);
  172.                 if(param.getInformazioneAssociataAlProgressivo()!=null){
  173.                     pstmt.setString(2, param.getInformazioneAssociataAlProgressivo());
  174.                 }
  175.                 rs = pstmt.executeQuery();
  176.                 if(rs == null) {
  177.                     pstmt.close();
  178.                     log.error("Creazione serial non riuscita: ResultSet is null?");
  179.                     throw new UtilsException("Creazione serial non riuscita: ResultSet is null?");      
  180.                 }
  181.                 boolean exist = rs.next();
  182.                 if(!exist) {
  183.                     if(JDBCUtilities.isTransactionIsolationSerializable(conDB.getTransactionIsolation(), tipoDatabase)==false) {
  184.                         rowNotExistsAndSerializableLevelNotFound = true;
  185.                         continue;
  186.                     }
  187.                 }

  188.                 // incremento se esiste
  189.                 if(exist){
  190.                     progressivoTmp = rs.getString(columnPrg);
  191.                     for (int i = 0; i < param.getSizeBuffer(); i++) {
  192.                         try{
  193.                             ApacheIdentifierGenerator generator = new ApacheIdentifierGenerator();
  194.                             generatorConfig.setInitialStringValue(progressivoTmp);
  195.                             generator.initialize(generatorConfig);
  196.                             progressivoTmp = generator.newID().getAsString();
  197.                             valuesGenerated.add(progressivoTmp);
  198.                         }catch(Exception eMaxRaggiunto){
  199.                             if(valuesGenerated.size()<=0){
  200.                                 throw eMaxRaggiunto;
  201.                             }
  202.                             else{
  203.                                 break; // utilizzo gli identificativi che ho generato fino ad ora.
  204.                             }
  205.                         }
  206.                     }
  207.                 }      
  208.                 rs.close();
  209.                 pstmt.close();

  210.                 if(exist==false){
  211.                     // First Value
  212.                     ApacheIdentifierGenerator generator = new ApacheIdentifierGenerator();
  213.                     generatorConfig.setSize(param.getSize());
  214.                     generator.initialize(generatorConfig);
  215.                     progressivoTmp = generator.newID().getAsString();
  216.                     // CREO PRIMO COUNT!
  217.                     StringBuilder queryInsert = new StringBuilder();
  218.                     ISQLQueryObject sqlInsert = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
  219.                     sqlInsert.addInsertTable(table);
  220.                     sqlInsert.addInsertField(columnPrg, "?");
  221.                     sqlInsert.addInsertField(columnProtocollo, "?");
  222.                     if(param.getInformazioneAssociataAlProgressivo()!=null){
  223.                         sqlInsert.addInsertField(columnInfoAssociata, "?");
  224.                     }
  225. //                  queryInsert.append("INSERT INTO "+table+" ("+columnPrg+","+columnProtocollo+columnCondition+") ");
  226. //                  queryInsert.append(" VALUES ( ? , ? "+columnValueCondition+")");
  227.                     queryInsert.append(sqlInsert.createSQLInsert());
  228.                     //System.out.println("INSERT ["+queryInsert.toString()+"]");
  229.                     pstmtInsert = conDB.prepareStatement(queryInsert
  230.                             .toString());
  231.                     pstmtInsert.setString(1, progressivoTmp);
  232.                     pstmtInsert.setString(2, protocollo);
  233.                     if(param.getInformazioneAssociataAlProgressivo()!=null){
  234.                         pstmtInsert.setString(3, param.getInformazioneAssociataAlProgressivo());
  235.                     }
  236.                     pstmtInsert.execute();
  237.                     pstmtInsert.close();
  238.                    
  239.                     valuesGenerated.add(progressivoTmp);
  240.                    
  241.                 }else{
  242.                     // Incremento!
  243.                     StringBuilder queryUpdate = new StringBuilder();
  244.                     ISQLQueryObject sqlUpdate = SQLObjectFactory.createSQLQueryObject(tipoDatabase);
  245.                     sqlUpdate.addUpdateTable(table);
  246.                     sqlUpdate.addUpdateField(columnPrg, "?");
  247.                     sqlUpdate.setANDLogicOperator(true);
  248.                     sqlUpdate.addWhereCondition(columnProtocollo+"=?");
  249.                     if(param.getInformazioneAssociataAlProgressivo()!=null){
  250.                         sqlUpdate.addWhereCondition(columnInfoAssociata+"=?");
  251.                     }
  252. //                  queryUpdate.append("UPDATE ");
  253. //                  queryUpdate.append(table);
  254. //                  queryUpdate.append(" SET "+columnPrg+" = ? WHERE "+columnProtocollo+"=?"+condition);
  255.                     queryUpdate.append(sqlUpdate.createSQLUpdate());
  256.                     //System.out.println("UPDATE ["+queryInsert.toString()+"]");
  257.                     pstmtInsert = conDB.prepareStatement(queryUpdate
  258.                             .toString());
  259.                     pstmtInsert.setString(1, progressivoTmp);
  260.                     pstmtInsert.setString(2, protocollo);
  261.                     if(param.getInformazioneAssociataAlProgressivo()!=null){
  262.                         pstmtInsert.setString(3, param.getInformazioneAssociataAlProgressivo());
  263.                     }
  264.                     pstmtInsert.execute();
  265.                     pstmtInsert.close();
  266.                 }

  267.                 // Chiusura Transazione
  268.                 conDB.commit();

  269.                 // ID Costruito
  270.                 idBuildOK = true;

  271.             } catch(Throwable e) {
  272.                 ps.append("********* Exception Iteration ["+iteration+"] **********\n");
  273.                 String msg = e.getMessage(); // per evitare out of memory
  274.                 if(msg==null){
  275.                     msg = "NULL-MESSAGE";
  276.                 }
  277.                 if(messageException.contains(msg)){
  278.                     ps.append("Message already occurs: "+msg);
  279.                 }
  280.                 else{
  281.                     e.printStackTrace(ps);
  282.                     messageException.add(msg);
  283.                 }
  284.                 ps.append("\n\n");
  285.                
  286.                 if(infoStatistics!=null){
  287.                     infoStatistics.addErrorSerializableAccess(e);
  288.                 }
  289.                
  290.                 if(e!=null && Utilities.existsInnerException(e, MaxReachedException.class)){
  291.                     maxValueAndWrapDisabled = true;
  292.                 }
  293.                
  294.                 //System.out.println("ERRORE: "+e.getMessage());
  295.                 //log.info("ERROR GET SERIAL SQL ["+e.getMessage()+"]");
  296.                 try{
  297.                     if( rs != null )
  298.                         rs.close();
  299.                 } catch(Exception er) {}
  300.                 try{
  301.                     if( pstmt != null )
  302.                         pstmt.close();
  303.                 } catch(Exception er) {}
  304.                 try{
  305.                     if( pstmtInsert != null )
  306.                         pstmtInsert.close();
  307.                 } catch(Exception er) {}
  308.                 try{
  309.                     conDB.rollback();
  310.                 } catch(Exception er) {}
  311.             }

  312.             if(idBuildOK == false){
  313.                 // Per aiutare ad evitare conflitti
  314.                 try{
  315.                     int intervalloDestro = checkIntervalloJDBC;
  316.                     if(param.isSerializableNextIntervalTimeMsIncrementMode()){
  317.                         intervalloDestro = intervalloDestro + (iteration*param.getSerializableNextIntervalTimeMsIncrement());
  318.                         if(intervalloDestro>param.getMaxSerializableNextIntervalTimeMs()){
  319.                             intervalloDestro = param.getMaxSerializableNextIntervalTimeMs();
  320.                         }
  321.                     }
  322.                    
  323.                     int sleep = getRandom().nextInt(intervalloDestro);
  324.                     //System.out.println("Sleep: "+sleep);
  325.                     Utilities.sleep(sleep); // random
  326.                 }catch(Exception eRandom){}
  327.             }
  328.         }

  329.         try{
  330.             if( ps != null ){
  331.                 ps.flush();
  332.             }
  333.         } catch(Exception er) {}
  334.         try{
  335.             if( out != null ){
  336.                 out.flush();
  337.             }
  338.         } catch(Exception er) {}
  339.         try{
  340.             if( ps != null ){
  341.                 ps.close();
  342.             }
  343.         } catch(Exception er) {}
  344.         try{
  345.             if( out != null ){
  346.                 out.close();
  347.             }
  348.         } catch(Exception er) {}
  349.        
  350.         if(maxValueAndWrapDisabled){
  351.             String msgError = "Max Value of identifier has been reached";
  352.             log.error(msgError+": "+out.toString()); // in out è presente l'intero stackTrace
  353.             throw new UtilsException(msgError);
  354.         }
  355.        
  356.         if(rowNotExistsAndSerializableLevelNotFound){
  357.             String msgError = "Raw not exists and serializable level is disabled";
  358.             log.error(msgError); // in out è presente l'intero stackTrace
  359.             throw new UtilsException(msgError);
  360.         }
  361.        
  362.         if(idBuildOK==false || progressivoTmp==null){
  363.             String msgError = "Creazione serial non riuscita: l'accesso serializable non ha permesso la creazione del numero sequenziale";
  364.             log.error(msgError+": "+out.toString()); // in out è presente l'intero stackTrace
  365.             throw new UtilsException(msgError);
  366.         }

  367.         String vRet = valuesGenerated.remove(0);
  368.        
  369.         if(valuesGenerated.size()>0){
  370.             IDSerialGeneratorBuffer.putAll(valuesGenerated,IDSerialGenerator_alphanumeric.class,param.getInformazioneAssociataAlProgressivo());
  371.         }
  372.        
  373.         //System.out.println("GET ["+vRet+"] AND SET BUFFER AT SIZE ["+valuesGenerated.size()+"]");
  374.        
  375.         return vRet;
  376.        
  377.     }
  378.    
  379. }