/**
* OpenSpotLight - Open Source IT Governance Platform
*
* Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA
* or third-party contributors as indicated by the @author tags or express
* copyright attribution statements applied by the authors. All third-party
* contributions are distributed under license by CARAVELATECH CONSULTORIA E
* TECNOLOGIA EM INFORMATICA LTDA.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
***********************************************************************
* OpenSpotLight - Plataforma de Governança de TI de Código Aberto
*
* Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA
* EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta
* @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor.
* Todas as contribuições de terceiros estão distribuídas sob licença da
* CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA.
*
* Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os
* termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software
* Foundation.
*
* Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA
* GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA
* FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes.
*
* Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este
* programa; se não, escreva para:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.openspotlight.federation.finder;
import static java.util.Collections.emptySet;
import static org.openspotlight.common.util.Exceptions.logAndReturn;
import static org.openspotlight.common.util.Exceptions.logAndReturnNew;
import static org.openspotlight.common.util.PatternMatcher.isMatchingWithoutCaseSentitiveness;
import static org.openspotlight.federation.finder.db.DatabaseSupport.createConnection;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.language.DefaultTemplateLexer;
import org.openspotlight.common.exception.ConfigurationException;
import org.openspotlight.common.util.Exceptions;
import org.openspotlight.domain.ArtifactSource;
import org.openspotlight.domain.DbArtifactSource;
import org.openspotlight.federation.domain.artifact.Artifact;
import org.openspotlight.federation.domain.artifact.db.DatabaseType;
import org.openspotlight.federation.finder.db.ColumnsNamesForMetadataSelect;
import org.openspotlight.federation.finder.db.DatabaseMetadataScript;
import org.openspotlight.federation.finder.db.DatabaseMetadataScript.DatabaseArtifactNameHandler;
import org.openspotlight.federation.finder.db.DatabaseMetadataScript.DatabaseStreamHandler;
import org.openspotlight.federation.finder.db.DatabaseMetadataScriptManager;
import org.openspotlight.federation.finder.db.ScriptType;
import org.openspotlight.federation.template.CustomizedStringTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractDatabaseArtifactFinder extends AbstractOriginArtifactLoader {
private static Logger logger = LoggerFactory.getLogger(AbstractDatabaseArtifactFinder.class);
private final Map<DbArtifactSource, Connection> connectionMap = new ConcurrentHashMap<DbArtifactSource, Connection>();
/**
* Execute the statement as a normal SQL query or as a {@link CallableStatement} if the script starts with '{'.
*
* @param sql the sql
* @param connection the connection
* @return the result set
* @throws SQLException if anything wrong happens
*/
static ResultSet executeStatement(final String sql,
final Connection connection)
throws SQLException {
ResultSet rs;
if (sql.trim().startsWith("{")) {
rs = connection.prepareCall(sql).executeQuery();
} else {
rs = connection.prepareStatement(sql).executeQuery();
}
return rs;
}
/**
* Fill name based on the column names configured on {@link DatabaseMetadataScript} xml file.
*
* @param script the script
* @param resultSet the result set
* @param nameHandler the name handler
* @return the artifact name
* @throws SQLException the SQL exception
*/
private String fillName(final DatabaseMetadataScript script,
final ResultSet resultSet,
final DatabaseArtifactNameHandler nameHandler)
throws SQLException {
final StringBuilder buffer = new StringBuilder("/");
String catalogColumnName = script.getColumnAliasMap().get(ColumnsNamesForMetadataSelect.catalog_name);
String nameColumnName = script.getColumnAliasMap().get(ColumnsNamesForMetadataSelect.name);
String schemaColumnName = script.getColumnAliasMap().get(ColumnsNamesForMetadataSelect.schema_name);
catalogColumnName = catalogColumnName != null ? catalogColumnName : ColumnsNamesForMetadataSelect.catalog_name.name();
nameColumnName = nameColumnName != null ? nameColumnName : ColumnsNamesForMetadataSelect.name.name();
schemaColumnName = schemaColumnName != null ? schemaColumnName : ColumnsNamesForMetadataSelect.schema_name.name();
final String catalog = resultSet.getString(catalogColumnName);
final String name =
nameHandler == null ? resultSet.getString(nameColumnName) : nameHandler.fixName(resultSet.getString(nameColumnName));
final String schema = resultSet.getString(schemaColumnName);
buffer.append(schema);
buffer.append('/');
buffer.append(script.getScriptType().name());
buffer.append('/');
if (catalog != null && !"".equals(catalog.trim())) {
buffer.append(catalog);
buffer.append('/');
}
buffer.append(name);
final String result = buffer.toString();
return result;
}
protected synchronized Connection getConnectionFromSource(final DbArtifactSource dbBundle)
throws Exception {
Connection conn = connectionMap.get(dbBundle);
if (conn == null) {
conn = createConnection(dbBundle);
connectionMap.put(dbBundle, conn);
}
return conn;
}
@Override
protected synchronized void internalCloseResources() {
final ArrayList<Connection> connections = new ArrayList<Connection>(connectionMap.values());
for (final Connection conn: connections) {
try {
conn.close();
} catch (final Exception e) {
Exceptions.catchAndLog(e);
}
}
connectionMap.clear();
}
@Override
protected <A extends Artifact> boolean internalIsMaybeChanged(final ArtifactSource source,
final String artifactName,
final A oldOne)
throws Exception {
return true;
}
@Override
protected <A extends Artifact> Set<String> internalRetrieveOriginalNames(final Class<A> type,
final ArtifactSource source,
final String initialPath)
throws Exception {
final DbArtifactSource dbBundle = (DbArtifactSource) source;
try {
final Connection conn = getConnectionFromSource(dbBundle);
synchronized (conn) {
final Set<String> loadedNames = new HashSet<String>();
try {
final DatabaseType databaseType = dbBundle.getType();
for (final ScriptType scriptType: ScriptType.values()) {
if (!scriptType.acceptType(type)) {
continue;
}
final DatabaseMetadataScript scriptDescription =
DatabaseMetadataScriptManager.INSTANCE
.getScript(
databaseType,
scriptType);
if (scriptDescription == null) {
continue;
}
final Class<? extends DatabaseArtifactNameHandler> dataHandlerType =
scriptDescription.getNameHandlerClass();
final DatabaseArtifactNameHandler nameHandler =
dataHandlerType != null ? dataHandlerType.newInstance() : null;
final ResultSet resultSet = executeStatement(scriptDescription.getDataSelect(), conn);
walkingOnResult: while (resultSet.next()) {
final String result = fillName(scriptDescription, resultSet, nameHandler);
if (nameHandler != null) {
final boolean shouldProcess = nameHandler.shouldIncludeName(result, scriptType, resultSet);
if (!shouldProcess) {
continue walkingOnResult;
}
}
if (isMatchingWithoutCaseSentitiveness(result, initialPath != null ? initialPath + "**" : "**")
&& scriptType.acceptName(result)) {
loadedNames.add(result);
if (logger.isDebugEnabled()) {
logger.debug("loading " + result + " due to its matching with "
+ initialPath + "**");
}
} else {
if (logger.isDebugEnabled()) {
logger
.debug("not loading " + result
+ " due to its not matching with " + initialPath + "**");
}
}
}
resultSet.close();
}
return loadedNames;
} catch (final Exception e) {
logAndReturnNew(e, ConfigurationException.class);
}
}
} catch (final Exception e) {
logAndReturnNew(e, ConfigurationException.class);
}
return emptySet();
}
@Override
protected boolean isMultithreaded() {
return false;
}
/**
* Loads the stream content by using a sql statement to fill it.
*
* @param catalog the catalog
* @param schema the schema
* @param name the name
* @param scriptDescription the script description
* @param streamHandler the stream handler
* @return the stream loaded from a sql query
* @throws Exception the exception
*/
protected byte[] loadFromSql(final String catalog,
final String schema,
final String name,
final DatabaseMetadataScript scriptDescription,
final DatabaseStreamHandler streamHandler,
final Connection conn)
throws Exception {
final Map<ColumnsNamesForMetadataSelect, String> columnValues =
new EnumMap<ColumnsNamesForMetadataSelect, String>(
ColumnsNamesForMetadataSelect.class);
columnValues.put(ColumnsNamesForMetadataSelect.catalog_name, catalog);
columnValues.put(ColumnsNamesForMetadataSelect.schema_name, schema);
columnValues.put(ColumnsNamesForMetadataSelect.name, name);
final StringTemplate template = new StringTemplate(scriptDescription.getContentSelect(), DefaultTemplateLexer.class);
for (final Map.Entry<ColumnsNamesForMetadataSelect, String> entry: columnValues.entrySet()) {
template.setAttribute(entry.getKey().name(), entry.getValue());
}
if (streamHandler != null) {
streamHandler.beforeFillTemplate(schema, scriptDescription.getScriptType(), catalog, name, template, conn);
}
final String sql = template.toString();
ResultSet resultSet = null;
try {
resultSet = executeStatement(sql, conn);
if (resultSet.next()) {
final int columnToUse =
scriptDescription.getContentColumnToUse() != null ? scriptDescription.getContentColumnToUse().intValue() : 1;
final String content = resultSet.getString(columnToUse);
return content != null ? content.getBytes() : null;
}
} catch (final Exception e) {
logAndReturn("Error on Sql " + sql, e);
} finally {
if (resultSet != null) {
resultSet.close();
}
}
return null;
}
/**
* Loads the stream content by using a {@link StringTemplate} and a sql statement to fill it.
*
* @param catalog the catalog
* @param schema the schema
* @param name the name
* @param scriptDescription the script description
* @param streamHandler the stream handler
* @return the stream loaded from a sql query and its {@link StringTemplate}
* @throws Exception the exception
*/
protected byte[] loadFromTemplate(final String catalog,
final String schema,
final String name,
final DatabaseMetadataScript scriptDescription,
final DatabaseStreamHandler streamHandler,
final Connection conn)
throws Exception {
final Map<ColumnsNamesForMetadataSelect, String> columnValues =
new EnumMap<ColumnsNamesForMetadataSelect, String>(
ColumnsNamesForMetadataSelect.class);
columnValues.put(ColumnsNamesForMetadataSelect.catalog_name, catalog);
columnValues.put(ColumnsNamesForMetadataSelect.schema_name, schema);
columnValues.put(ColumnsNamesForMetadataSelect.name, name);
final StringTemplate template = new StringTemplate(scriptDescription.getTemplatesSelect(), DefaultTemplateLexer.class);
for (final Map.Entry<ColumnsNamesForMetadataSelect, String> entry: columnValues.entrySet()) {
template.setAttribute(entry.getKey().name(), entry.getValue());
}
final String sql = template.toString();
ResultSet resultSet = null;
try {
final String templateString = scriptDescription.getTemplate();
final CustomizedStringTemplate contentTemplate = new CustomizedStringTemplate(templateString,
DefaultTemplateLexer.class);
for (final Map.Entry<ColumnsNamesForMetadataSelect, String> entry: columnValues.entrySet()) {
contentTemplate.setAttributeArray(entry.getKey().name(), entry.getValue());
}
int count = 0;
boolean hasAnyResult = false;
String attributeName = null;
if (sql != null && sql.trim().length() != 0) {
resultSet = executeStatement(sql, conn);
while (resultSet.next()) {
final List<String> columnsFromDatabase = new ArrayList<String>();
if (!hasAnyResult) {
hasAnyResult = true;
final StringBuilder baseForTemplate = new StringBuilder("detail.{");
final ResultSetMetaData metadata = resultSet.getMetaData();
count = metadata.getColumnCount();
for (int i = 1; i <= count; i++) {
final String columnName = metadata.getColumnLabel(i).toLowerCase();
final String content = resultSet.getString(i);
contentTemplate.setAttribute(columnName, content);
columnsFromDatabase.add(content);
baseForTemplate.append(columnName);
if (i != count) {
baseForTemplate.append(',');
}
}
baseForTemplate.append('}');
attributeName = baseForTemplate.toString();
} else {
for (int i = 1; i <= count; i++) {
columnsFromDatabase.add(resultSet.getString(i));
}
}
final Object[] valuesAsArray = columnsFromDatabase.toArray();
contentTemplate.setAttributeArray(attributeName, valuesAsArray);
}
resultSet.close();
if (!hasAnyResult) {
logAndReturn(new IllegalStateException("no result on " + sql));
}
}
if (streamHandler != null) {
streamHandler.beforeFillTemplate(schema, scriptDescription.getScriptType(), catalog, name, contentTemplate, conn);
}
final String result = contentTemplate.toString();
return result.getBytes();
} finally {
if (resultSet != null) {
resultSet.close();
}
}
}
}