/*
* StreamCruncher: Copyright (c) 2006-2008, Ashwin Jayaprakash. All Rights Reserved.
* Contact: ashwin {dot} jayaprakash {at} gmail {dot} com
* Web: http://www.StreamCruncher.com
*
* This file is part of StreamCruncher.
*
* StreamCruncher is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* StreamCruncher 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 StreamCruncher. If not, see <http://www.gnu.org/licenses/>.
*/
package streamcruncher.innards;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import streamcruncher.api.artifact.IndexSpec;
import streamcruncher.api.artifact.MiscSpec;
import streamcruncher.api.artifact.RowSpec;
import streamcruncher.api.artifact.RunningQuery;
import streamcruncher.api.artifact.TableFQN;
import streamcruncher.api.artifact.TableSpec;
import streamcruncher.boot.Component;
import streamcruncher.boot.Registry;
import streamcruncher.innards.core.InstreamNotificationRendezvous;
import streamcruncher.innards.core.stream.InStream;
import streamcruncher.innards.core.stream.OutStream;
import streamcruncher.innards.db.DatabaseInterface;
import streamcruncher.innards.file.FileManager;
import streamcruncher.innards.file.FileManagerException;
import streamcruncher.util.LoggerManager;
import streamcruncher.util.undo.Helper;
/*
* Author: Ashwin Jayaprakash Date: Jan 2, 2006 Time: 9:59:59 AM
*/
public class InnardsManager implements Component {
protected final ConcurrentMap<String, InStream> inStreams;
protected final ConcurrentMap<String, RunningQuery> runningQueries;
protected final ConcurrentMap<String, OutStream> outStreams;
protected final InstreamNotificationRendezvous notificationRendezvous;
protected FileManager artifactManager;
public InnardsManager() {
inStreams = new ConcurrentHashMap<String, InStream>();
runningQueries = new ConcurrentHashMap<String, RunningQuery>();
outStreams = new ConcurrentHashMap<String, OutStream>();
notificationRendezvous = new InstreamNotificationRendezvous();
}
public void start(Object... params) throws Exception {
Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(
InnardsManager.class.getName());
logger.log(Level.INFO, "Started");
// -------------
artifactManager = Registry.getImplFor(FileManager.class);
}
public void stop() throws Exception {
artifactManager = null;
// -------------
Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(
InnardsManager.class.getName());
logger.log(Level.INFO, "Stopped");
}
// --------------
/**
* @param spec
* @param autoPurge
* @param blockSize
* @param startup
* @throws InnardsManagerException
*/
public void registerInStream(String name, RowSpec rowSpec, int blockSize, boolean startup)
throws InnardsManagerException {
if (inStreams.containsKey(name) == false) {
InStream inStream = new InStream(name, rowSpec, blockSize, notificationRendezvous);
inStreams.put(name, inStream);
if (startup == false) {
try {
artifactManager.saveInStream(inStream, false);
}
catch (FileManagerException e) {
inStreams.remove(name);
throw new InnardsManagerException(e);
}
}
}
else {
throw new InnardsManagerException("The InStream: " + name
+ " has already been registered.");
}
}
/**
* @param schema
* @param name
* @return
* @throws InnardsManagerException
*/
public void unregisterInStream(String name) throws InnardsManagerException {
InStream inStream = inStreams.get(name);
if (inStream != null) {
if (inStream.getListeners().isEmpty() == false) {
throw new InnardsManagerException("The InStream: " + name
+ ", could not be unregistered as there are"
+ " some Queries listening on it.");
}
// --------------
inStreams.remove(name);
artifactManager.deleteInStream(inStream);
}
}
/**
* @param name
* {@link InStream#getName()}
* @return
* @see #getRegisteredInStream(String)
*/
public InStream getRegisteredInStream(String name) {
return inStreams.get(name);
}
public ConcurrentMap<String, InStream> getAllRegisteredInStreams() {
return inStreams;
}
// --------------
/**
* Uses {@link RunningQuery#getName()} as the Key.
*
* @param runningQuery
* @param startup
* @return
* @throws InnardsManagerException
*/
public void registerRunningQuery(final RunningQuery runningQuery, boolean startup)
throws InnardsManagerException {
Helper helper = new Helper();
final String key = runningQuery.getName();
if (runningQueries.containsKey(key) == false) {
DatabaseInterface databaseInterface = Registry.getImplFor(DatabaseInterface.class);
try {
if (startup == false) {
artifactManager.saveRunningQuery(runningQuery, false);
helper.registerUndoEntry(new Helper.UndoRunner() {
public void undo() throws Exception {
artifactManager.deleteRunningQuery(runningQuery);
}
});
}
TableSpec resultTableSpec = null;
TableFQN resultTableFQN = runningQuery.getResultTableFQN();
Map<String, TableSpec> tableFQNAndSpecMap = runningQuery.getTableFQNAndSpecMap();
Set<String> keys = tableFQNAndSpecMap.keySet();
for (String fqn : keys) {
final TableSpec tableSpec = tableFQNAndSpecMap.get(fqn);
if (fqn.equals(resultTableFQN.getFQN())) {
resultTableSpec = tableSpec;
}
if (tableSpec.isVirtual()) {
continue;
}
if (startup && databaseInterface.dbPreservesArtifactsOnShutdown()) {
runDropDDL(tableSpec);
}
runCreateDDL(tableSpec);
helper.registerUndoEntry(new Helper.UndoRunner() {
public void undo() throws Exception {
InnardsManager.this.runDropDDL(tableSpec);
}
});
}
// -------------
runningQueries.put(key, runningQuery);
helper.registerUndoEntry(new Helper.UndoRunner() {
public void undo() throws Exception {
runningQueries.remove(key);
}
});
// -------------
Collection<InStream> theInStreams = inStreams.values();
for (final InStream stream : theInStreams) {
stream.afterRegisteringRQ(runningQuery);
helper.registerUndoEntry(new Helper.UndoRunner() {
public void undo() throws Exception {
stream.beforeUnregisteringRQ(runningQuery);
}
});
}
// -------------
OutStream outStream = new OutStream(runningQuery.getName(), resultTableSpec);
outStreams.put(runningQuery.getName(), outStream);
}
catch (Exception e) {
helper.undo(true);
throw new InnardsManagerException(e);
}
}
else {
throw new InnardsManagerException("The RunningQuery: " + runningQuery.getName()
+ " has already been registered.");
}
}
/**
* @param name
*/
public void unregisterRunningQuery(String name) {
RunningQuery runningQuery = runningQueries.get(name);
if (runningQuery == null) {
return;
}
// -------------
outStreams.remove(runningQuery.getName());
// -------------
Collection<InStream> theInStreams = inStreams.values();
for (InStream stream : theInStreams) {
stream.beforeUnregisteringRQ(runningQuery);
}
// -------------
runningQueries.remove(name);
// -------------
Map<String, TableSpec> tableFQNAndSpecMap = runningQuery.getTableFQNAndSpecMap();
Set<String> keys = tableFQNAndSpecMap.keySet();
for (String fqn : keys) {
TableSpec tableSpec = tableFQNAndSpecMap.get(fqn);
if (tableSpec.isVirtual()) {
continue;
}
try {
runDropDDL(tableSpec);
}
catch (InnardsManagerException e) {
Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(
InnardsManager.class.getName());
logger.log(Level.WARNING, "", e);
}
}
// -------------
artifactManager.deleteRunningQuery(runningQuery);
}
public ConcurrentMap<String, RunningQuery> getAllRegisteredRQs() {
return runningQueries;
}
public RunningQuery getRegisteredRQs(String name) {
return runningQueries.get(name);
}
// --------------
/**
* @param queryName
* @return
*/
public OutStream getRegisteredOutStream(String queryName) {
return outStreams.get(queryName);
}
public ConcurrentMap<String, OutStream> getAllRegisteredOutStreams() {
return outStreams;
}
// --------------
/**
* @return Returns the notificationRendezvous.
*/
public InstreamNotificationRendezvous getNotificationRendezvous() {
return notificationRendezvous;
}
// --------------
public void runCreateDDL(TableSpec spec) throws InnardsManagerException {
LinkedList<String> commands = new LinkedList<String>();
LinkedList<String> dropCommands = new LinkedList<String>();
commands.add(spec.constructCreateCommand());
dropCommands.add(spec.constructDropCommand());
IndexSpec[] indexSpecs = spec.getIndexSpecs();
for (IndexSpec spec2 : indexSpecs) {
commands.add(spec2.constructCreateCommand());
dropCommands.add(spec2.constructDropCommand());
}
MiscSpec[] others = spec.getOtherClauses();
for (MiscSpec spec2 : others) {
commands.add(spec2.constructCreateCommand());
dropCommands.add(spec2.constructDropCommand());
}
// --------------
DatabaseInterface databaseInterface = Registry.getImplFor(DatabaseInterface.class);
Connection connection = null;
Statement statement = null;
int counter = 0;
try {
connection = databaseInterface.createConnection();
statement = connection.createStatement();
for (Iterator<String> iter = commands.iterator(); iter.hasNext();) {
String ddl = iter.next();
statement.execute(ddl);
SQLWarning warning = statement.getWarnings();
if (warning != null) {
throw warning;
}
counter++;
}
}
catch (Throwable t) {
Collections.reverse(dropCommands);
// Remove commands that were not executed yet.
int remove = dropCommands.size() - counter;
while (remove > 0) {
dropCommands.remove(0);
remove--;
}
for (Iterator<String> iter = dropCommands.iterator(); iter.hasNext();) {
String ddl = iter.next();
try {
statement.execute(ddl);
SQLWarning warning = statement.getWarnings();
if (warning != null) {
logDDLError(spec, ddl, warning);
}
}
catch (Throwable t1) {
logDDLError(spec, ddl, t1);
}
}
// ---------------
throw new InnardsManagerException("Error occurred while executing DDL for: "
+ spec.getFQN(), t);
}
finally {
streamcruncher.innards.util.Helper.closeStatement(statement);
streamcruncher.innards.util.Helper.closeConnection(connection);
}
}
public void runDropDDL(TableSpec spec) throws InnardsManagerException {
LinkedList<String> commands = new LinkedList<String>();
MiscSpec[] others = spec.getOtherClauses();
for (MiscSpec spec2 : others) {
commands.add(spec2.constructDropCommand());
}
IndexSpec[] indexSpecs = spec.getIndexSpecs();
for (IndexSpec spec2 : indexSpecs) {
commands.add(spec2.constructDropCommand());
}
commands.add(spec.constructDropCommand());
// --------------
DatabaseInterface databaseInterface = Registry.getImplFor(DatabaseInterface.class);
Connection connection = null;
Statement statement = null;
try {
connection = databaseInterface.createConnection();
statement = connection.createStatement();
for (Iterator<String> iter = commands.iterator(); iter.hasNext();) {
String ddl = iter.next();
try {
statement.execute(ddl);
SQLWarning warning = statement.getWarnings();
if (warning != null) {
logDDLError(spec, ddl, warning);
}
}
catch (Throwable t1) {
logDDLError(spec, ddl, t1);
}
}
}
catch (SQLException e) {
throw new InnardsManagerException("Error occurred while executing Drop-DDL for: "
+ spec.getFQN(), e);
}
finally {
streamcruncher.innards.util.Helper.closeStatement(statement);
streamcruncher.innards.util.Helper.closeConnection(connection);
}
}
private void logDDLError(TableSpec spec, String ddl, Throwable t1) {
String msg = "Error/Warning occurred while executing Drop-DDL/clean-up for: "
+ spec.getFQN();
msg = msg + System.getProperty("line.separator");
msg = msg
+ "This command will have to be executed manually on the DB if the artifact exists: ";
msg = msg + System.getProperty("line.separator") + "[" + ddl + "]";
Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(
InnardsManager.class.getName());
logger.log(Level.WARNING, msg, t1);
}
}