/*******************************************************************************
* Copyright (c) 2014, 2015 Wind River Systems, Inc.
* 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.tcf.remote.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteConnectionType;
import org.eclipse.remote.core.IRemoteConnectionWorkingCopy;
import org.eclipse.remote.core.exception.RemoteConnectionException;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.te.core.interfaces.IConnectable;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.events.ChangeEvent;
import org.eclipse.tcf.te.runtime.events.EventManager;
import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener;
import org.eclipse.tcf.te.tcf.locator.interfaces.IPeerModelListener;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProperties;
import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
import org.eclipse.tcf.te.tcf.remote.core.activator.CoreBundleActivator;
public class TCFConnectionManager implements IPeerModelListener, IEventListener {
public static final TCFConnectionManager INSTANCE = new TCFConnectionManager();
private final Map<String, TCFConnection> fConnections = Collections.synchronizedMap(new HashMap<String, TCFConnection>());
private IRemoteConnectionType fConnectionType;
private int fInitialized = 0;
public TCFConnection mapConnection(IRemoteConnection rc) {
if (rc == null)
return null;
if (!rc.getConnectionType().getId().equals(TCFConnection.CONNECTION_TYPE_ID))
return null;
synchronized(fConnections) {
String name = rc.getName();
TCFConnection result = fConnections.get(name);
if (result == null) {
result = new TCFConnection(rc);
fConnections.put(name, result);
}
return result;
}
}
public void setConnectionType(IRemoteConnectionType connectionType) {
synchronized(fConnections) {
fConnectionType = connectionType;
initialize();
syncConnections();
}
}
public void initialize() {
synchronized (fConnections) {
if (fInitialized > 0)
return;
fInitialized = 1;
Protocol.invokeLater(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
EventManager.getInstance().addEventListener(TCFConnectionManager.this, ChangeEvent.class);
IPeerModel peerModel = ModelManager.getPeerModel();
peerModel.addListener(TCFConnectionManager.this);
synchronized (fConnections) {
for (IPeerNode peerNode : peerModel.getPeerNodes()) {
String name = peerNode.getPeer().getName();
TCFConnection connection = fConnections.get(name);
if (connection == null) {
fConnections.put(name, new TCFConnection(peerNode));
} else {
connection.setPeerNode(peerNode);
}
}
fInitialized = 2;
syncConnections();
fConnections.notifyAll();
}
}
});
}
}
void syncConnections() {
if (fConnectionType != null && fInitialized == 2) {
// Remove all connections without a peer
for (IRemoteConnection rc : new ArrayList<IRemoteConnection>(fConnectionType.getConnections())) {
String name = rc.getName();
TCFConnection connection = fConnections.get(name);
if (connection == null || connection.getPeerNode() == null) {
try {
fConnectionType.removeConnection(rc);
} catch (RemoteConnectionException e) {
CoreBundleActivator.logError("Cannot remove remote connection.", e); //$NON-NLS-1$
}
fConnections.remove(name);
}
}
// Add connections with peers
for (Iterator<TCFConnection> it = fConnections.values().iterator(); it.hasNext(); ) {
TCFConnection connection = it.next();
IPeerNode peerNode = connection.getPeerNode();
if (peerNode == null) {
it.remove();
} else {
addRemoteConnection(connection);
}
}
}
}
private void addRemoteConnection(TCFConnection connection) {
if (fConnectionType == null)
return;
String name = connection.getName();
if (fConnectionType.getConnection(name) == null) {
try {
IRemoteConnectionWorkingCopy wc = fConnectionType.newConnection(name);
IRemoteConnection rc = wc.save();
connection.setRemoteConnection(rc);
} catch (RemoteConnectionException e) {
CoreBundleActivator.logError("Cannot add remote connection.", e); //$NON-NLS-1$
}
}
}
public void waitForInitialization(IProgressMonitor monitor) {
synchronized (fConnections) {
if (fInitialized == 2)
return;
if (fInitialized == 0)
initialize();
while (fInitialized != 2) {
if (monitor.isCanceled())
return;
try {
fConnections.wait(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
@Override
public void modelChanged(IPeerModel model, IPeerNode peerNode, boolean added) {
String name = peerNode.getPeer().getName();
if (added) {
synchronized (fConnections) {
TCFConnection connection = fConnections.get(name);
if (connection != null) {
connection.setPeerNode(peerNode);
} else {
connection = new TCFConnection(peerNode);
fConnections.put(name, connection);
}
addRemoteConnection(connection);
}
} else {
TCFConnection connection = fConnections.remove(name);
if (connection != null) {
connection.setConnectedTCF(false);
IRemoteConnection rc = connection.getRemoteConnection();
if (rc != null) {
try {
rc.getConnectionType().removeConnection(rc);
} catch (RemoteConnectionException e) {
CoreBundleActivator.logError("Cannot remove remote connection.", e); //$NON-NLS-1$
}
}
}
}
}
@Override
public void modelDisposed(IPeerModel model) {
fConnections.clear();
}
@Override
public void eventFired(EventObject event) {
final ChangeEvent changeEvent = (ChangeEvent) event;
final Object source = changeEvent.getSource();
if (source instanceof IPeerNode && IPeerNodeProperties.PROPERTY_CONNECT_STATE.equals(changeEvent.getEventId())) {
IPeerNode peerNode = (IPeerNode) source;
TCFConnection connection = fConnections.get(peerNode.getPeer().getName());
if (connection != null) {
Object val= changeEvent.getNewValue();
boolean connected = val instanceof Number && ((Number) val).intValue() == IConnectable.STATE_CONNECTED;
connection.setConnectedTCF(connected);
}
}
}
void open(IPeerNode peerNode, IProgressMonitor monitor) throws RemoteConnectionException {
final boolean[] done = {false};
Callback callback = new Callback() {
@Override
protected void internalDone(Object caller, IStatus status) {
synchronized (done) {
done[0] = true;
done.notify();
}
}
};
synchronized (done) {
peerNode.changeConnectState(IConnectable.ACTION_CONNECT, callback, monitor);
try {
if (!done[0])
done.wait();
} catch (InterruptedException e) {
throw new RemoteConnectionException(e);
}
}
if (peerNode.getConnectState() != IConnectable.STATE_CONNECTED) {
IStatus status = callback.getStatus();
if (status != null && !status.isOK()) {
String msg = status.getMessage();
if (msg != null && msg.length() > 0) {
throw new RemoteConnectionException(msg, status.getException());
}
}
throw new RemoteConnectionException(Messages.TCFConnectionManager_errorCannotConnect, status != null ? status.getException() : null);
}
}
void close(IPeerNode peerNode) {
peerNode.changeConnectState(IConnectable.ACTION_DISCONNECT, new Callback(), null);
}
}