/*
* 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.core.stream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import streamcruncher.api.artifact.RowSpec;
import streamcruncher.api.artifact.RunningQuery;
import streamcruncher.api.artifact.TableFQN;
import streamcruncher.boot.Registry;
import streamcruncher.innards.core.EventBucket;
import streamcruncher.innards.core.InstreamNotificationRendezvous;
import streamcruncher.innards.core.filter.FilteredTable;
import streamcruncher.innards.core.partition.PartitionedTable;
import streamcruncher.innards.db.DatabaseInterface;
import streamcruncher.util.TwoDAppendOnlyList;
/*
* Author: Ashwin Jayaprakash Date: Jan 2, 2006 Time: 10:20:27 AM
*/
public class InStream implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Although a Table never gets created, for the sake of being consistent
* with {@linkplain FilteredTable Filters} and
* {@linkplain PartitionedTable Partitions}, the InStream gets created
* with the Schema from {@link DatabaseInterface#getSchema()}.
*/
protected final String schema;
protected final String name;
protected final RowSpec rowSpec;
protected final int blockSize;
/**
* Map of {@link RunningQuery#getName()} - {@link InStreamListenerData}.
*/
protected final ConcurrentMap<String, InStreamListenerData> listeners;
protected final TwoDAppendOnlyList streamData;
protected final InstreamNotificationRendezvous notificationRendezvous;
// --------------------------
protected final int hashCode;
protected final String str;
// --------------------------
protected final AtomicBoolean notificationLock;
/**
* @param name
* @param rowSpec
* @param blockSize
* @param notificationRendezvous
*/
public InStream(String name, RowSpec rowSpec, int blockSize,
InstreamNotificationRendezvous notificationRendezvous) {
DatabaseInterface dbInterface = Registry.getImplFor(DatabaseInterface.class);
this.schema = dbInterface.getSchema();
this.name = name;
this.rowSpec = rowSpec;
this.blockSize = blockSize;
this.listeners = new ConcurrentHashMap<String, InStreamListenerData>();
this.streamData = new TwoDAppendOnlyList(blockSize);
this.notificationRendezvous = notificationRendezvous;
this.notificationLock = new AtomicBoolean(false);
// --------------------------
int hash = (schema + ".").hashCode();
hash = hash + (37 * (name + " ").hashCode());
this.hashCode = hash;
this.str = schema + "." + name;
}
/**
* @return Same instance, but empty {@link #listeners}.
* @throws ObjectStreamException
*/
protected Object writeReplace() throws ObjectStreamException {
this.listeners.clear();
return this;
}
// --------------------------
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @return Returns the schema.
*/
public String getSchema() {
return schema;
}
public String getFQN() {
return (schema == null) ? name : str;
}
public RowSpec getRowSpec() {
return rowSpec;
}
public int getBlockSize() {
return blockSize;
}
/**
* @return Returns the listenerData.
*/
public ConcurrentMap<String, InStreamListenerData> getListeners() {
return listeners;
}
public boolean attemptNotificationLock() {
return notificationLock.compareAndSet(false, true);
}
public boolean isNotificationLockSet() {
return notificationLock.get();
}
public void unsetNotificationLock() {
notificationLock.set(false);
}
/**
* @return Returns the list of Events.
*/
public TwoDAppendOnlyList getStreamData() {
return streamData;
}
// --------------------------
public void afterRegisteringRQ(RunningQuery runningQuery) {
LinkedList<EventBucket> bucketsOnCurrStream = null;
EventBucket[] eventBuckets = runningQuery.getEventBuckets();
for (EventBucket bucket : eventBuckets) {
bucketsOnCurrStream = addAsListener(bucketsOnCurrStream, bucket);
}
if (bucketsOnCurrStream != null) {
EventBucket[] thisStreamsBuckets = bucketsOnCurrStream
.toArray(new EventBucket[bucketsOnCurrStream.size()]);
for (EventBucket bucket : thisStreamsBuckets) {
bucket.setEventBucketClient(runningQuery);
bucket.setStreamDataBuffer(getStreamData());
}
InStreamListenerData listenerData = new InStreamListenerData(runningQuery.getName(),
thisStreamsBuckets);
listeners.put(runningQuery.getName(), listenerData);
}
}
protected LinkedList<EventBucket> addAsListener(LinkedList<EventBucket> bucketsOnCurrStream,
EventBucket bucket) {
TableFQN currTableFQN = bucket.getSourceTableFQN();
String thisFQN = this.getFQN();
String thatFQN = currTableFQN.getFQN();
// Aliases will be ignored. We just need the FQN.
if (thisFQN.equals(thatFQN)) {
if (bucketsOnCurrStream == null) {
bucketsOnCurrStream = new LinkedList<EventBucket>();
}
bucketsOnCurrStream.add(bucket);
}
return bucketsOnCurrStream;
}
public void beforeUnregisteringRQ(RunningQuery runningQuery) {
listeners.remove(runningQuery.getName());
}
// --------------------------
/**
* <b>Note:</b> This operation works by assuming that there will be only
* one Thread invoking this method at a time.
*
* @param event
*/
public void addEvent(Object[] event) {
// todo Verify event array is not null, column size is expected etc.
streamData.add(event);
if (isNotificationLockSet() == false) {
notificationRendezvous.sendNotification(this);
}
}
/**
* <b>Note:</b> This operation works by assuming that there will be only
* one Thread invoking this method at a time.
*
* @param events
*/
public void addEvents(Object[][] events) {
// todo Verify event array is not null, column size is expected etc.
streamData.add(events);
if (isNotificationLockSet() == false) {
notificationRendezvous.sendNotification(this);
}
}
// --------------------------
@Override
public boolean equals(Object obj) {
if (obj instanceof InStream) {
InStream that = (InStream) obj;
String thisStr = toString();
String thatStr = that.toString();
return thisStr.equals(thatStr);
}
return false;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
return str;
}
}