/** * Copyright 2002-2015 SCOOP Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.copperengine.ext.persistent; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.copperengine.core.CopperRuntimeException; import org.copperengine.core.DependencyInjector; import org.copperengine.core.EngineIdProvider; import org.copperengine.core.batcher.Batcher; import org.copperengine.core.batcher.RetryingTxnBatchRunner; import org.copperengine.core.batcher.impl.BatcherImpl; import org.copperengine.core.common.WorkflowRepository; import org.copperengine.core.persistent.DatabaseDialect; import org.copperengine.core.persistent.DerbyDbDialect; import org.copperengine.core.persistent.H2Dialect; import org.copperengine.core.persistent.MySqlDialect; import org.copperengine.core.persistent.OracleDialect; import org.copperengine.core.persistent.OracleSimpleDialect; import org.copperengine.core.persistent.PersistentScottyEngine; import org.copperengine.core.persistent.PostgreSQLDialect; import org.copperengine.core.persistent.ScottyDBStorage; import org.copperengine.core.persistent.ScottyDBStorageInterface; import org.copperengine.core.persistent.txn.CopperTransactionController; import org.copperengine.core.persistent.txn.TransactionController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; /** * Utility class to create a RDBMS (e.g. oracle or mysql) {@link PersistentScottyEngine}. * <p> * Usage is quite simple, e.g. using a SupplierDependencyInjector: * * <pre> * RdbmsEngineFactory<SupplierDependencyInjector> engineFactory = new * RdbmsEngineFactory<SupplierDependencyInjector>(Arrays.asList("package.of.copper.workflow.classes")) { * * protected SupplierDependencyInjector createDependencyInjector() { * return new SupplierDependencyInjector(); * } * * protected DataSource createDataSource() { * return ...; * } * }; * engineFactory.getEngine().startup(); * </pre> * * @author austermann * * @param <T> * type of DependencyInjector which shall be used from the created engine */ public abstract class RdbmsEngineFactory<T extends DependencyInjector> extends AbstractPersistentEngineFactory<T> { private static final Logger logger = LoggerFactory.getLogger(RdbmsEngineFactory.class); protected final Supplier<DataSource> dataSource; protected final Supplier<BatcherImpl> batcher; private int numberOfBatcherThreads = 4; public RdbmsEngineFactory(List<String> wfPackges) { super(wfPackges); dataSource = Suppliers.memoize(new Supplier<DataSource>() { @Override public DataSource get() { logger.info("Creating DataSource..."); return createDataSource(); } }); batcher = Suppliers.memoize(new Supplier<BatcherImpl>() { @Override public BatcherImpl get() { logger.info("Creating Batcher..."); return createBatcher(); } }); } public void setNumberOfBatcherThreads(int numberOfBatcherThreads) { this.numberOfBatcherThreads = numberOfBatcherThreads; } protected abstract DataSource createDataSource(); protected BatcherImpl createBatcher() { @SuppressWarnings("rawtypes") RetryingTxnBatchRunner batchRunner = new RetryingTxnBatchRunner(); batchRunner.setDataSource(dataSource.get()); BatcherImpl batcher = new BatcherImpl(numberOfBatcherThreads); batcher.setBatchRunner(batchRunner); batcher.startup(); return batcher; } @Override protected TransactionController createTransactionController() { CopperTransactionController txnController = new CopperTransactionController(); txnController.setDataSource(dataSource.get()); return txnController; } @Override protected ScottyDBStorageInterface createDBStorage() { DatabaseDialect dialect = createDatabaseDialect(); final ScottyDBStorage dbStorage = new ScottyDBStorage(); dbStorage.setDialect(dialect); dbStorage.setTransactionController(transactionController.get()); dbStorage.setBatcher(batcher.get()); return dbStorage; } protected DatabaseDialect createDatabaseDialect() { DatabaseDialect dialect = createDialect(dataSource.get(), workflowRepository.get(), engineIdProvider.get()); dialect.startup(); return dialect; } protected DatabaseDialect createDialect(DataSource ds, WorkflowRepository wfRepository, EngineIdProvider engineIdProvider) { Connection c = null; try { c = ds.getConnection(); String name = c.getMetaData().getDatabaseProductName(); if ("oracle".equalsIgnoreCase(name)) { if (OracleDialect.schemaMatches(c)) { OracleDialect dialect = new OracleDialect(); dialect.setWfRepository(wfRepository); dialect.setEngineIdProvider(engineIdProvider); dialect.setMultiEngineMode(false); return dialect; } else { OracleSimpleDialect dialect = new OracleSimpleDialect(); dialect.setWfRepository(wfRepository); return dialect; } } if ("Apache Derby".equalsIgnoreCase(name)) { DerbyDbDialect dialect = new DerbyDbDialect(); dialect.setDataSource(ds); dialect.setWfRepository(wfRepository); return dialect; } if ("H2".equalsIgnoreCase(name)) { H2Dialect dialect = new H2Dialect(); dialect.setDataSource(ds); dialect.setWfRepository(wfRepository); return dialect; } if ("MySQL".equalsIgnoreCase(name)) { MySqlDialect dialect = new MySqlDialect(); dialect.setWfRepository(wfRepository); return dialect; } if ("PostgreSQL".equalsIgnoreCase(name)) { PostgreSQLDialect dialect = new PostgreSQLDialect(); dialect.setWfRepository(wfRepository); return dialect; } throw new Error("No dialect available for DBMS " + name); } catch (Exception e) { throw new CopperRuntimeException("Unable to create dialect", e); } finally { if (c != null) try { c.close(); } catch (SQLException e) { logger.error("unable to close connection", e); } } } public Batcher getBatcher() { return batcher.get(); } @Override public void destroyEngine() { super.destroyEngine(); batcher.get().shutdown(); } }