/*******************************************************************************
* Copyright (c) 2004, 2007 Composent, Inc., Peter Nehrer, Boris Bokowski.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Composent, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.provider.datashare;
import java.io.Serializable;
import java.util.Map;
import org.eclipse.ecf.core.events.IContainerConnectedEvent;
import org.eclipse.ecf.core.events.IContainerDisconnectedEvent;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.sharedobject.*;
import org.eclipse.ecf.core.sharedobject.events.ISharedObjectMessageEvent;
import org.eclipse.ecf.core.util.*;
import org.eclipse.ecf.datashare.*;
import org.eclipse.ecf.datashare.events.*;
import org.eclipse.ecf.internal.provider.datashare.Activator;
public class BaseChannel extends TransactionSharedObject implements IChannel {
public static final String RECEIVER_ID_PROPERTY = BaseChannel.class.getName();
static class ChannelMsg implements Serializable {
private static final long serialVersionUID = 9065358269778864152L;
byte[] channelData = null;
ChannelMsg() {
//
}
ChannelMsg(byte[] data) {
this.channelData = data;
}
byte[] getData() {
return channelData;
}
public String toString() {
StringBuffer buf = new StringBuffer("BaseChannel.ChannelMsg["); //$NON-NLS-1$
buf.append("data=").append(getData()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buf.toString();
}
}
protected IChannelListener listener;
/**
* Primary copy implementation of channel class constructor
*
* @param config
* the ISharedObjectTransactionConfig associated with this new
* host instance
* @param listener
* the listener associated with this channel instance
*/
public BaseChannel(ISharedObjectTransactionConfig config, IChannelListener listener) {
super(config);
setChannelListener(listener);
}
/**
* Replica implementation of channel class constructor
*
*/
public BaseChannel() {
super();
}
protected void setChannelListener(IChannelListener l) {
this.listener = l;
}
protected void trace(String msg) {
Trace.trace(Activator.PLUGIN_ID, msg);
}
/**
* Override of TransasctionSharedObject.initialize(). This method is called
* on both the host and the replicas during initialization. <b>Subclasses
* that override this method should be certain to call super.initialize() as
* the first thing in their own initialization so they get the
* initialization defined by TransactionSharedObject and BaseSharedObject.</b>
*
* @throws SharedObjectInitException
* if initialization should fail
*/
protected void initialize() throws SharedObjectInitException {
super.initialize();
if (!isPrimary())
initializeReplicaChannel();
addEventProcessor(new IEventProcessor() {
public boolean processEvent(Event event) {
trace("processEvent(" + event + ")"); //$NON-NLS-1$ //$NON-NLS-2$
IChannelListener l = getListener();
if (event instanceof IContainerConnectedEvent) {
if (l != null)
l.handleChannelEvent(createChannelGroupJoinEvent(true, ((IContainerConnectedEvent) event).getTargetID()));
} else if (event instanceof IContainerDisconnectedEvent) {
if (l != null)
l.handleChannelEvent(createChannelGroupDepartEvent(true, ((IContainerDisconnectedEvent) event).getTargetID()));
} else if (event instanceof ISharedObjectMessageEvent) {
BaseChannel.this.handleMessageEvent((ISharedObjectMessageEvent) event);
}
return false;
}
});
trace("initialize()"); //$NON-NLS-1$
}
/**
* Override of TransactionSharedObject.getAdapter()
*/
public Object getAdapter(Class clazz) {
if (clazz.equals(IChannel.class)) {
return this;
}
return super.getAdapter(clazz);
}
IChannelConnectEvent createChannelGroupJoinEvent(final boolean hasJoined, final ID targetID) {
return new IChannelConnectEvent() {
public ID getTargetID() {
return targetID;
}
public ID getChannelID() {
return getID();
}
public String toString() {
StringBuffer buf = new StringBuffer("ChannelConnectEvent["); //$NON-NLS-1$
buf.append("channelid=").append(getChannelID()).append(";targetid=").append(getTargetID()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return buf.toString();
}
};
}
IChannelDisconnectEvent createChannelGroupDepartEvent(final boolean hasJoined, final ID targetID) {
return new IChannelDisconnectEvent() {
public ID getTargetID() {
return targetID;
}
public ID getChannelID() {
return getID();
}
public String toString() {
StringBuffer buf = new StringBuffer("ChannelDisconnectEvent["); //$NON-NLS-1$
buf.append("channelid=").append(getChannelID()).append(";targetid=").append(getTargetID()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return buf.toString();
}
};
}
Event handleMessageEvent(final ISharedObjectMessageEvent event) {
Object eventData = event.getData();
ChannelMsg channelMsg = null;
IChannelListener l = getListener();
if (eventData instanceof ChannelMsg) {
channelMsg = (ChannelMsg) eventData;
final byte[] channelData = channelMsg.getData();
if (channelData != null) {
if (l == null)
return event;
listener.handleChannelEvent(new IChannelMessageEvent() {
public ID getFromContainerID() {
return event.getRemoteContainerID();
}
public byte[] getData() {
return channelData;
}
public ID getChannelID() {
return getID();
}
public String toString() {
StringBuffer buf = new StringBuffer("ChannelMessageEvent["); //$NON-NLS-1$
buf.append("channelid=").append(getChannelID()).append(";fromid=").append(getFromContainerID()).append(";data=").append(getData()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
return buf.toString();
}
});
// Discontinue processing of this event...we are it
return null;
}
}
return event;
}
// Implementation of org.eclipse.ecf.datashare.IChannel
/*
* (non-Javadoc)
*
* @see org.eclipse.ecf.datashare.IChannel#sendMessage(byte[])
*/
public void sendMessage(byte[] message) throws ECFException {
sendMessage(null, message);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ecf.datashare.IChannel#sendMessage(org.eclipse.ecf.core.identity.ID,
* byte[])
*/
public void sendMessage(ID receiver, byte[] message) throws ECFException {
try {
getContext().sendMessage(receiver, new ChannelMsg(message));
} catch (Exception e) {
throw new ECFException("send message exception", e); //$NON-NLS-1$
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ecf.datashare.IAbstractChannel#getListener()
*/
public synchronized IChannelListener getListener() {
return listener;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ecf.datashare.IAbstractChannel#setListener(org.eclipse.ecf.datashare.IChannelListener)
*/
public IChannelListener setListener(IChannelListener listener) {
IChannelListener oldListener = getListener();
setChannelListener(listener);
return oldListener;
}
// Subclass API
/**
* Receive and process channel events. This method can be overridden by
* subclasses to process channel events in a sub-class specific manner.
*
* @param channelEvent
* the IChannelEvent to receive and process
*/
protected void receiveUndeliveredChannelEvent(IChannelEvent channelEvent) {
if (isPrimary())
trace("host.receiveUndeliveredChannelEvent(" + channelEvent + ";containerid=" + getContext().getLocalContainerID() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
else
trace("replica.receiveUndeliveredChannelEvent(" + channelEvent + ";containerid=" + getContext().getLocalContainerID() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Override of BaseSharedObject.getReplicaDescription. Note this method
* should be overridden by subclasses that wish to specify the type of the
* replica created.
*
* @param targetContainerID
* the ID of the target container for subsequentreplica creation.
* If null, the target is <b>all</b> current group members
* @return ReplicaSharedObjectDescripton to be used for creating remote
* replica of this host shared object. If null, no create message
* will be sent to the target container.
*/
protected ReplicaSharedObjectDescription getReplicaDescription(ID targetContainerID) {
return new ReplicaSharedObjectDescription(getClass(), getID(), getConfig().getHomeContainerID(), getConfig().getProperties());
}
/**
* Initialize replicas of this channel. This method is only called if
* isPrimary() returns false. It is called from within the initialize
* method, immediately after super.initialize but before the listener for
* this channel is notified of initialization. If this method throws a
* SharedObjectInitException, then initialization of the replica is halted
* and the remote transaction creating the replica will be aborted.
* <p>
* Note that this implementation checks for the existence of the
* RECEIVER_ID_PROPERTY on the replica's properties, and if the property
* contains a valid ID will
* <ul>
* <li>lookup the IChannel on the given container via
* IChannelContainerAdapter.getID(ID)</li>
* <li>call IChannel.getListener() to retrieve the listener for the channel
* returned</li>
* <li>set the listener for this object to the value returned from
* IChannel.getListener()</li>
* </ul>
*
* @throws SharedObjectInitException
* if the replica initialization should fail
*/
protected void initializeReplicaChannel() throws SharedObjectInitException {
Map properties = getConfig().getProperties();
ID rcvr = null;
try {
rcvr = (ID) properties.get(RECEIVER_ID_PROPERTY);
} catch (ClassCastException e) {
throw new SharedObjectInitException("Receiver ID property value must be of type ID", e); //$NON-NLS-1$
}
if (rcvr != null) {
// Now...get local channel container first...throw if we can't get
// it
IChannelContainerAdapter container = (IChannelContainerAdapter) getContext().getAdapter(IChannelContainerAdapter.class);
if (container == null)
throw new SharedObjectInitException("channel container adapter must not be null"); //$NON-NLS-1$
// Now get receiver IChannel...throw if we can't get it
final IChannel receiver = container.getChannel(rcvr);
if (receiver == null)
throw new SharedObjectInitException("channel receiver must not be null"); //$NON-NLS-1$
setChannelListener(receiver.getListener());
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ecf.datashare.IAbstractChannel#dispose()
*/
public void dispose() {
destroySelfLocal();
}
}