/* * Copyright (c) 2011, Rickard Öberg. All Rights Reserved. * Copyright (c) 2012, Paul Merlin. All Rights Reserved. * * 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.qi4j.library.sql.datasource; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.qi4j.api.composite.PropertyMapper; import org.qi4j.api.entity.EntityBuilder; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.service.ImportedServiceDescriptor; import org.qi4j.api.service.ServiceImporter; import org.qi4j.api.service.ServiceImporterException; import org.qi4j.api.structure.Module; import org.qi4j.api.unitofwork.NoSuchEntityException; import org.qi4j.api.unitofwork.UnitOfWork; import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; import org.qi4j.api.usecase.UsecaseBuilder; import org.qi4j.library.circuitbreaker.CircuitBreaker; import org.qi4j.library.conversion.values.EntityToValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractDataSourceServiceImporterMixin<PooledDataSourceType extends DataSource> implements ServiceImporter<DataSource>, DataSourceServiceImporterActivation { protected static final Logger LOGGER = LoggerFactory.getLogger( AbstractDataSourceServiceImporterMixin.class ); private final Map<String, DataSourceConfigurationValue> configs = new HashMap<String, DataSourceConfigurationValue>(); private final Map<String, PooledDataSourceType> pools = new HashMap<String, PooledDataSourceType>(); private final Map<DataSource, CircuitBreaker> circuitBreakers = new HashMap<DataSource, CircuitBreaker>(); @Structure protected Module module; @Service private EntityToValue entityToValue; @Override public final void passivateDataSourceService() throws Exception { for ( PooledDataSourceType pool : pools.values() ) { passivateDataSourcePool( pool ); } pools.clear(); configs.clear(); circuitBreakers.clear(); } @Override public final synchronized DataSource importService( final ImportedServiceDescriptor importedServiceDescriptor ) throws ServiceImporterException { PooledDataSourceType pool = pools.get( importedServiceDescriptor.identity() ); if ( pool == null ) { try { DataSourceConfigurationValue config = getConfiguration( importedServiceDescriptor.identity() ); if ( !config.enabled().get() ) { // Not started throw new ServiceImporterException( "DataSource not enabled" ); } // Instantiate pool pool = setupDataSourcePool( config ); pools.put( importedServiceDescriptor.identity(), pool ); LOGGER.info( "Starting up DataSource '" + importedServiceDescriptor.identity() + "' for: {}@{}", config.username().get(), config.url().get() ); } catch ( Exception e ) { throw new ServiceImporterException( e ); } // Test the pool ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( null ); try { pool.getConnection().close(); LOGGER.info( "Database for DataSource is up!" ); } catch ( SQLException e ) { LOGGER.warn( "Database for DataSource " + importedServiceDescriptor.identity() + " is not currently available" ); throw new ServiceImporterException( "Database for DataSource " + importedServiceDescriptor.identity() + " is not currently available", e ); } finally { Thread.currentThread().setContextClassLoader( cl ); } } // Check if circuitbreaker is used final CircuitBreaker circuitBreaker = importedServiceDescriptor.metaInfo( CircuitBreaker.class ); if ( circuitBreaker != null ) { DataSource wrappedDataSource = DataSources.wrapWithCircuitBreaker( importedServiceDescriptor.identity(), pool, circuitBreaker ); circuitBreakers.put( pool, circuitBreaker ); return wrappedDataSource; } else { return pool; } } private DataSourceConfigurationValue getConfiguration( String identity ) throws InstantiationException { DataSourceConfigurationValue config = configs.get( identity ); if ( config == null ) { UnitOfWork uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( "Create DataSource pool configuration" ) ); try { DataSourceConfiguration configEntity = uow.get( DataSourceConfiguration.class, identity ); config = entityToValue.convert( DataSourceConfigurationValue.class, configEntity ); } catch ( NoSuchEntityException e ) { EntityBuilder<DataSourceConfiguration> configBuilder = uow.newEntityBuilder( DataSourceConfiguration.class, identity ); // Check for defaults String s = identity + ".properties"; InputStream asStream = DataSourceConfiguration.class.getClassLoader().getResourceAsStream( s ); if ( asStream != null ) { try { PropertyMapper.map( asStream, configBuilder.instance() ); } catch ( IOException e1 ) { uow.discard(); InstantiationException exception = new InstantiationException( "Could not read underlying Properties file." ); exception.initCause( e1 ); throw exception; } } DataSourceConfiguration configEntity = configBuilder.newInstance(); config = entityToValue.convert( DataSourceConfigurationValue.class, configEntity ); // save try { uow.complete(); } catch ( UnitOfWorkCompletionException e2 ) { InstantiationException exception = new InstantiationException( "Could not save configuration in JavaPreferences." ); exception.initCause( e2 ); throw exception; } } configs.put( identity, config ); } return config; } @Override public final boolean isAvailable( DataSource instance ) { if ( pools.containsValue( instance ) ) { CircuitBreaker circuitBreaker = circuitBreakers.get( instance ); if ( circuitBreaker != null ) { return circuitBreaker.isOn(); } else { return true; } } else { return false; } } protected abstract PooledDataSourceType setupDataSourcePool( DataSourceConfigurationValue configuration ) throws Exception; protected abstract void passivateDataSourcePool( PooledDataSourceType dataSourcePool ) throws Exception; }