Package org.xmlBlaster.contrib.dbwriter

Source Code of org.xmlBlaster.contrib.dbwriter.DbWriter

/*------------------------------------------------------------------------------
Name:      TestResultSetToXmlConverter.java
Project:   org.xmlBlasterProject:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.contrib.dbwriter;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import java.util.logging.Level;

import org.xmlBlaster.contrib.I_ChangePublisher;
import org.xmlBlaster.contrib.I_ContribPlugin;
import org.xmlBlaster.contrib.I_Info;
import org.xmlBlaster.contrib.I_Update;
import org.xmlBlaster.contrib.InfoHelper;
import org.xmlBlaster.contrib.PropertiesInfo;
import org.xmlBlaster.contrib.db.I_DbPool;
import org.xmlBlaster.contrib.dbwriter.info.SqlInfo;
import org.xmlBlaster.contrib.replication.ReplicationConstants;
import org.xmlBlaster.util.pool.I_PoolManager;
import org.xmlBlaster.util.qos.ClientProperty;

/**
*
* @author Michele Laghi mailto:michele@laghi.eu
*/
public class DbWriter implements I_Update {

   public final static String INITIAL_UPDATE_EVENT_PRE = "initialUpdatePre";
   public final static String INITIAL_UPDATE_EVENT_POST = "initialUpdatePost";
   private static Logger log = Logger.getLogger(DbWriter.class.getName());
   public final static String DB_POOL_KEY = "db.pool";
   public final static String CASE_SENSITIVE_KEY = "dbWriter.caseSensitive";
   /** dbWriter.quoteColumnNames=true: Add quotes around the column names like "COMMENT" instead
    * of COMMENT, this is e.g. needed for DB2 sinks running on a host (OS390).
    * Defaults to false.
    */
   public final static String QUOTE_COLUMN_NAMES_KEY = "dbWriter.quoteColumnNames";
   private I_Info info;
   private I_ChangePublisher eventEngine;
   private I_Parser parser;
   private I_Writer writer;
   private I_DbPool dbPool;
   private boolean poolOwner;
   private boolean isAlive;
   // this is used mainly when testing to get an event when an update is finished
   private I_Update registeredForUpdates;
   private String charSet = null;
  
   /**
    * Default constructor, you need to call {@link #init} thereafter.
    */
   public DbWriter() {
   }

   /**
    * Convenience constructor, creates a processor for changes, calls {@link #init}
    * @param info Configuration
    * @throws Exception Can be of any type
    */
   public DbWriter(I_Info info) throws Exception {
      init(info);
   }
  
   /**
    * Creates an Instance of a DbWriter with the given configuration. If loginName is null, then
    * it is created with no manipulation of the mom.loginName and mom.password properties, othewise
    * a clone of the I_Info is taken and a DbWriter instance is created with the loginName specified
    * in the argument list.
    *
    * @param masterInfo
    * @param loginName
    * @param password
    * @return
    */
   public static DbWriter createSingleDbWriter(I_Info masterInfo, String loginName, String password) throws Exception {
      if (loginName == null || loginName.length() < 0) {
         log.warning("The configuration contains no 'mom.loginName' property, will only use one instance of DbWriter based on other configuration parameters");
         return new DbWriter(masterInfo);
      }
      I_Info instanceInfo = new PropertiesInfo(new Properties()); // could be another implementation too
      InfoHelper.fillInfoWithEntriesFromInfo(instanceInfo, masterInfo);
      instanceInfo.put("mom.loginName", loginName);
      instanceInfo.put("mom.password", password);
      log.fine("instancing a DbWriter for mom.loginName '" + loginName + "' with password='" + password + "'");
      return new DbWriter(instanceInfo);
   }
  
   /**
    * Creates a list of DbWriter instances. One instance for each mom.loginName entry found. Note that
    * the mom.loginName can contain a list of comma separated session names (or subject names). For
    * each such name an instance is created. If the mom.password entries are less than the mom.loginName
    * ones, the last password will be used for all undefined passwords.
    * @param info
    * @return
    * @throws Exception
    */
   public static List createDbWriters(I_Info masterInfo) throws Exception {
      // clone the I_Info object first
      List dbWriterList = new ArrayList();
      String loginNames = masterInfo.get("mom.loginName", null);
      if (loginNames == null || loginNames.length() < 0) {
         dbWriterList.add(createSingleDbWriter(masterInfo, null, null));
         return dbWriterList;
      }
      String passwords = masterInfo.get("mom.password", "secret");
      String password = "secret";
      StringTokenizer nameTokenizer = new StringTokenizer(loginNames, ",");
      StringTokenizer passwordTokenizer = new StringTokenizer(passwords, ",");
     
      ClassLoader cl = DbWriter.class.getClassLoader();
      createPool(cl, masterInfo);
      while (nameTokenizer.hasMoreTokens()) {
         String loginName = nameTokenizer.nextToken().trim();
         if (passwordTokenizer.hasMoreTokens())
            password = passwordTokenizer.nextToken().trim();
         if (loginName != null && loginName.length() > 0) {
            dbWriterList.add(createSingleDbWriter(masterInfo, loginName, password));
         }
      }
      log.info("Created and configured '" + dbWriterList.size() + "' instances of DbWriter with mom.loginName='" + loginNames + "'");
      return dbWriterList;
   }
  
   public static void setPoolOwner(DbWriter writer, boolean poolOwner) {
      if (writer != null)
         writer.poolOwner = poolOwner;
   }
  
   public boolean getPoolOwner() {
      return this.poolOwner;
   }

  
   private final static I_DbPool createPool(ClassLoader cl, I_Info info) throws Exception {
      I_DbPool dbPool = (I_DbPool)info.getObject("db.pool");
      if (dbPool == null) {
         String dbPoolClass = info.get("dbPool.class", "org.xmlBlaster.contrib.db.DbPool");
         if (dbPoolClass.length() > 0) {
             dbPool = (I_DbPool)cl.loadClass(dbPoolClass).newInstance();
             dbPool.init(info);
             if (log.isLoggable(Level.FINE)) log.fine(dbPoolClass + " created and initialized");
         }
         else
            throw new IllegalArgumentException("Couldn't initialize I_DbPool, please configure 'dbPool.class' to provide a valid JDBC access.");
         setPoolOwner(null, true);
         info.putObject(DB_POOL_KEY, dbPool);
      }
      return dbPool;
   }

   /**
    * Creates a processor for changes.
    * The alert producers need to be started later with a call to
    * {@link #startAlertProducers}
    * @param info Configuration
    * @throws Exception Can be of any type
    */
   public synchronized void init(I_Info info) throws Exception {
      if (info == null) throw new IllegalArgumentException("Missing configuration, info is null");
      this.info = info;
      this.info.putObject("org.xmlBlaster.contrib.dbwriter.DbWriter", this);
      ClassLoader cl = this.getClass().getClassLoader();
      this.dbPool = createPool(cl, info);

      // Now we load all plugins to do the job
      String momClass = this.info.get("mom.class", "org.xmlBlaster.contrib.MomEventEngine").trim();
      String parserClass = this.info.get("parser.class", "org.xmlBlaster.contrib.dbwriter.SqlInfoParser").trim();
      String writerClass = this.info.get("dbWriter.writer.class", "org.xmlBlaster.contrib.dbwriter.Writer").trim();
  
      if (parserClass.length() > 0) {
          this.parser = (I_Parser)cl.loadClass(parserClass).newInstance();
          this.parser.init(info);
          if (log.isLoggable(Level.FINE)) log.fine(parserClass + " created and initialized");
      }
      else
         log.info("Couldn't initialize I_Parser, please configure 'parser.class' if you need a conversion.");
     
      if (writerClass.length() > 0) {
         this.writer = (I_Writer)cl.loadClass(writerClass).newInstance();
         this.writer.init(info);
          if (log.isLoggable(Level.FINE)) log.fine(writerClass + " created and initialized");
      }
      else
         log.severe("Couldn't initialize I_Storer, please configure 'dbWriter.writer.class'.");

      this.isAlive = true;
      if (momClass.length() > 0) {
         this.eventEngine = (I_ChangePublisher)cl.loadClass(momClass).newInstance();
         this.eventEngine.registerAlertListener(this, null);
         this.eventEngine.init(info);
         if (log.isLoggable(Level.FINE)) log.fine(momClass + " created and initialized");
      }
      else
         log.severe("Couldn't initialize I_EventEngine, please configure 'mom.class'.");
      this.charSet = info.get("charSet", null);
      if (log.isLoggable(Level.FINE)) log.fine("DbWriter created");
   }
  
   public I_Info getInfo() {
      return this.info;
   }

   private void shutdown(I_ContribPlugin plugin) {
      try {
         if (plugin != null)
            plugin.shutdown();
      }
      catch(Throwable e) {
         e.printStackTrace();
         log.warning(e.toString());
      }
   }
  
   /**
    * Cleanup resources.
    * @throws Exception Can be of any type
    */
   public synchronized void shutdown() {
      try {
         shutdown(this.eventEngine);
         this.isAlive = false;
         shutdown(this.parser);
         shutdown(this.writer);
         if (this.poolOwner && this.dbPool != null) {
            this.dbPool.shutdown();
            this.dbPool = null;
            this.info.putObject("db.pool", null);
         }
      }
      catch (Exception ex) {
         ex.printStackTrace();
         log.severe("An exception occured when shutting down: " + ex.getMessage());
      }
   }

   /**
    * Determines wether the message is a database dump or not. If it is a database dump it stores the content to a file,
    * otherwise it processes the message according to the normal processing flow.
    */
   public synchronized void update(String topic, InputStream is, Map attrMap) throws Exception {
      if (!this.isAlive) {
         throw new Exception("update topic='" + topic + "' happens when we not alive: \n");
      }
      try {
         ClientProperty dumpProp = (ClientProperty)attrMap.get(ReplicationConstants.DUMP_ACTION);
         ClientProperty endToRemoteProp = (ClientProperty)attrMap.get(ReplicationConstants.INITIAL_DATA_END_TO_REMOTE);
         ClientProperty initialDumpAsXml = (ClientProperty)attrMap.get(ReplicationConstants.INITIAL_DUMP_AS_XML);
         ClientProperty endOfTransition = (ClientProperty)attrMap.get(ReplicationConstants.END_OF_TRANSITION);
        
         if (dumpProp != null && initialDumpAsXml == null) { // initial dump (xml)
            this.writer.update(topic, is, attrMap);
         }
         else if (endToRemoteProp != null) { // initial dump, binary
            this.writer.update(topic, is, attrMap);
         }
         else if (endOfTransition != null) { // initial dum, binary, ready to import from file system (last file part arrived)
            this.writer.update(topic, is, attrMap);
         }
         else { // replication xml, build an SqlInfo from XML
            SqlInfo updateInfo = this.parser.parse(is, this.charSet);
            if (updateInfo == null) {
               log.warning("The entry was not for us");
               return;
            }
            ClientProperty keepTransactionOpenProp = (ClientProperty)attrMap.get(ReplicationConstants.KEEP_TRANSACTION_OPEN);
            if (keepTransactionOpenProp != null && keepTransactionOpenProp.getBooleanValue()) {
               // is the property already set ? No, then pass it to the info object
               if (updateInfo.getDescription().getAttribute(ReplicationConstants.KEEP_TRANSACTION_OPEN) == null) {
                  log.fine("Setting the property '" + ReplicationConstants.KEEP_TRANSACTION_OPEN + "' to true");
                  updateInfo.getDescription().setAttribute(keepTransactionOpenProp);
               }
               else
                  log.warning("The property '" + ReplicationConstants.KEEP_TRANSACTION_OPEN + "' was already set as a description attribute (will not overwrite it)");
            }
            this.writer.store(updateInfo);
         }
      }
      finally {
         if (this.registeredForUpdates != null)
            this.registeredForUpdates.update(topic, null, attrMap);
      }
   }
  
   public void registerForUpdates(I_Update registeredForUpdates) {
      this.registeredForUpdates = registeredForUpdates;
   }
  
  
}
TOP

Related Classes of org.xmlBlaster.contrib.dbwriter.DbWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.