if (!isAllowed())
return null;
if (this.xmlBlasterConnection == null) { // Login to other cluster node ...
ConnectQosData connectQosData = getNodeInfo().getConnectQosData();
// Reuse in a gateway the remote cluster node login's socket for our connection
// Only for protocol of types "socket*"
// ConnectQosSaxFactory->ClientProperty.ATTRIBUTE_TAG
//<address type='SOCKET' sessionId='4e56890ghdFzj0' pingInterval='10000' retries='-1' delay='10000'>
// <burstMode collectTime='400' maxEntries='20' maxBytes='-1' />
// <ptp>true</ptp>
// <attribute name='useRemoteLoginAsTunnel' type='boolean'>true</attribute>
//</address>
boolean useRemoteLoginAsTunnel = connectQosData.getAddress().getEnv("useRemoteLoginAsTunnel", false).getValue(); //"heron".equals(qos.getSessionName().getLoginName());
if (useRemoteLoginAsTunnel) { // The cluster master tries to tunnel using the slaves connection
// globalKey="ClusterManager[cluster]/SocketExecutorclient/heron/session/1"
// Aware: is unique only in remoteGlob
final String globalKey = SocketExecutor.getGlobalKey(connectQosData.getSessionName());
final String secretSessionId = null;
final int pubSessionId = 1;
// "client/avalon/session/1" (we are heron and want to re-use avalons connection)
// Dangerous Precond: The remote cluster logs in with subjectId==his-nodeId and pubSessionId=1
final SessionName sessionName = new SessionName(this.remoteGlob, null, nodeInfo.getId(), pubSessionId);
SessionInfo myRemotePartnerLogin = this.fatherGlob.getRequestBroker().getAuthenticate(secretSessionId).getSessionInfo(sessionName);
this.remoteGlob.addObjectEntry(globalKey, "dummyPlaceHolder");
log.info(sessionName.toString() + " Adding initial 'dummyPlaceHolder' entryKey=" + globalKey + " global.instanceId=" +this.remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
if (myRemotePartnerLogin == null) {
// Create the temporary SessionInfo until the real client arrives
String[] args = new String[0]; //{ "-queue/connection/defaultPlugin", "RAM,1.0" };
Global glob = this.remoteGlob; // Why?? .getClone(args);
String type = "SOCKET";
String version = "1.0";
String rawAddress = "socket://:7607";
boolean found = false;
I_Driver[] drivers = this.fatherGlob.getPluginRegistry().getPluginsOfInterfaceI_Driver();//register(pluginInfo.getId(), plugin);//getProtocolPluginManager().getPlugin(type, version)
for (int i=0; i<drivers.length; i++) {
if (drivers[i] instanceof SocketDriver) {
SocketDriver sd = (SocketDriver)drivers[i];
rawAddress = sd.getRawAddress();
type = sd.getType();
version = sd.getVersion();
found = true;
}
}
if (!found)
log.severe("No socket protocol driver found");
// TODO: How to avoid configuring the password (pass a flag to Authenticate?)
// TODO: Currently we can only configure loginName/password based credentials
// cluster/securityService/avalon=<securityService type='htpasswd' version='1.0'><user>avalon</user><passwd>secret</passwd></securityService>
String xml = this.fatherGlob.get("cluster/securityService/"+sessionName.getLoginName(), "", null, null);
if ("".equals(xml)) {
log.severe("To bootstrap an initial session of " + sessionName.getLoginName() + " cluster slave you need to give his password like this (adjust the password and the type if necessary): " +
"cluster/securityService/" + sessionName.getLoginName() + "=<securityService type='htpasswd' version='1.0'><user>" + sessionName.getLoginName() + "</user><passwd>secret</passwd></securityService>");
return null;
}
SecurityQos securityQos = new SecurityQos(glob, xml);
ConnectQos tmpQos = new ConnectQos(glob, sessionName.getRelativeName(), "");
tmpQos.getData().setSecurityQos(securityQos);
tmpQos.setSessionName(sessionName);
ClientQueueProperty prop = new ClientQueueProperty(glob, null);
prop.setType("RAM");
Address address = new Address(glob);
address.setDelay(40000L);
address.setRetries(-1);
address.setPingInterval(20000L);
address.setType(type);
address.setVersion(version);
//address.addClientProperty(new ClientProperty("useRemoteLoginAsTunnel", true));
address.addClientProperty(new ClientProperty("acceptRemoteLoginAsTunnel", true));
address.setRawAddress(rawAddress); // Address to find ourself
//address.addClientProperty(new ClientProperty("acceptRemoteLoginAsTunnel", "", "", ""+true));
prop.setAddress(address);
tmpQos.addClientQueueProperty(prop);
CallbackAddress cbAddress = new CallbackAddress(glob);
cbAddress.setDelay(40000L);
cbAddress.setRetries(-1);
cbAddress.setPingInterval(20000L);
cbAddress.setDispatcherActive(false);
cbAddress.setType(type);
cbAddress.setVersion(version);
//cbAddress.addClientProperty(new ClientProperty("useRemoteLoginAsTunnel", true));
cbAddress.addClientProperty(new ClientProperty("acceptRemoteLoginAsTunnel", true));
tmpQos.addCallbackAddress(cbAddress);
tmpQos.setPersistent(true);
glob.getXmlBlasterAccess().setServerNodeId(getId());
log.info("Creating temporary session " + sessionName.getRelativeName() + " until real cluster node "
+ glob.getXmlBlasterAccess().getServerNodeId() + " arrives");
glob.getXmlBlasterAccess().connect(tmpQos, new I_Callback() {
public String update(String cbSessionId, UpdateKey updateKey,
byte[] content, UpdateQos updateQos)
throws XmlBlasterException {
return null;
}
});
glob.getXmlBlasterAccess().leaveServer(null);
myRemotePartnerLogin = this.fatherGlob.getRequestBroker().getAuthenticate(secretSessionId).getSessionInfo(sessionName);
if (myRemotePartnerLogin == null) {
log.severe("Can't create session " + sessionName.getAbsoluteName());
return null;
}
}
DispatchManager mgr = myRemotePartnerLogin.getDispatchManager();
if (mgr != null) {
boolean fireInitial = true;
mgr.addConnectionStatusListener(new I_ConnectionStatusListener() {
// The !remote! node has logged in (not our client connection)
public void toAlive(DispatchManager dispatchManager, ConnectionStateEnum oldState) {
SessionInfo myRemotePartnerLogin = fatherGlob.getRequestBroker().getAuthenticate(secretSessionId).getSessionInfo(sessionName);
if (myRemotePartnerLogin != null && myRemotePartnerLogin.getAddressServer() != null) {
Object obj = myRemotePartnerLogin.getAddressServer().getCallbackDriver();
if (obj != null && obj instanceof CallbackSocketDriver) {
// cbDriver.callbackAddress: socket://192.168.1.20:8920
CallbackSocketDriver cbDriver = (CallbackSocketDriver)myRemotePartnerLogin.getAddressServer().getCallbackDriver();
log.info("toAlive(" + sessionName.getAbsoluteName() + ")... found existing session to back-tunnel '" + getId() + "' on address '" + myRemotePartnerLogin.getAddressServer().getRawAddress() + "' protocol=" + myRemotePartnerLogin.getAddressServer().getType() + " cbDriver-Handler " + ((cbDriver.getHandler()==null)?"null":cbDriver.getHandler().getAddressServer().getRawAddress()));
//log.severe("Register toAlive: CallbackSocketDriver.handler=" + cbDriver.getHandler());
remoteGlob.addObjectEntry(globalKey, cbDriver.getHandler());
log.info(sessionName.toString() + " Adding toAlive '" + cbDriver.getHandler() + "' entryKey=" + globalKey + " global.instanceId=" + remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
}
else {
log.info("toAlive(" + sessionName.getAbsoluteName() + ")... no CallbackSocketDriver to back-tunnel '" + getId() + "' found");
remoteGlob.addObjectEntry(globalKey, "dummyPlaceHolder");
log.info(sessionName.toString() + " Adding toAlive 'dummyPlaceHolder' entryKey=" + globalKey + " global.instanceId=" + remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
}
}
else {
log.info("toAlive(" + sessionName.getAbsoluteName() + ")... no session to back-tunnel '" + getId() + "' found");
remoteGlob.addObjectEntry(globalKey, "dummyPlaceHolder");
log.info(sessionName.toString() + " Adding toAlive 'dummyPlaceHolder' (myRemotePartnerLogin=null) entryKey=" + globalKey + " global.instanceId=" + remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
}
}
public void toPolling(DispatchManager dispatchManager, ConnectionStateEnum oldState) {
log.warning("toPolling(" + sessionName.getAbsoluteName() + ") for cluster back-tunnel ...");
remoteGlob.addObjectEntry(globalKey, "dummyPlaceHolder");
log.info(sessionName.toString() + " Adding toPolling 'dummyPlaceHolder' entryKey=" + globalKey + " global.instanceId=" + remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
if (oldState == ConnectionStateEnum.ALIVE)
ping(); // Force our client connection to POLLING as
// well
}
public void toDead(DispatchManager dispatchManager, ConnectionStateEnum oldState, XmlBlasterException xmlBlasterException) {
log.severe("toDead(" + sessionName.getAbsoluteName() + ") for cluster back-tunnel ...");
remoteGlob.addObjectEntry(globalKey, "dummyPlaceHolder");
log.info(sessionName.toString() + " Adding toDead 'dummyPlaceHolder' entryKey=" + globalKey + " global.instanceId=" + remoteGlob.getInstanceId() + "-" + remoteGlob.hashCode());
if (oldState == ConnectionStateEnum.ALIVE)
ping(); // Force our client connection to POLLING
}
public void toAliveSync(DispatchManager dispatchManager, ConnectionStateEnum oldState) {
}
}, fireInitial);
}
/* done by fireInitial
if (myRemotePartnerLogin != null && myRemotePartnerLogin.getAddressServer() != null) {
Object obj = myRemotePartnerLogin.getAddressServer().getCallbackDriver();
if (obj != null && obj instanceof CallbackSocketDriver) {
CallbackSocketDriver cbDriver = (CallbackSocketDriver)myRemotePartnerLogin.getAddressServer().getCallbackDriver();
this.remoteGlob.addObjectEntry(globalKey, cbDriver.getHandler());
}
}
*/
}
boolean acceptRemoteLoginAsTunnel = connectQosData.getAddress().getEnv("acceptRemoteLoginAsTunnel", false).getValue(); //"heron".equals(qos.getSessionName().getLoginName());
if (acceptRemoteLoginAsTunnel) { // The cluster slave accepts publish(), subscribe() etc callbacks
this.remoteGlob.addObjectEntry("ClusterManager[cluster]/I_Authenticate", this.fatherGlob.getAuthenticate());
this.remoteGlob.addObjectEntry("ClusterManager[cluster]/I_XmlBlaster", this.fatherGlob.getAuthenticate().getXmlBlaster());
}
this.xmlBlasterConnection = this.remoteGlob.getXmlBlasterAccess();
this.xmlBlasterConnection.setUserObject(this);
// force client side queue unique name, instead of setStorageIdStr()
this.xmlBlasterConnection.setServerNodeId(getId());
this.xmlBlasterConnection.registerConnectionListener(this);
final XmlBlasterAccess xbAccess = (XmlBlasterAccess)this.xmlBlasterConnection;
this.xmlBlasterConnection.registerPostSendListener(new I_PostSendListener() {
public void postSend(MsgQueueEntry[] msgQueueEntries) {
}
// For example on user.security.authorization.notAuthorized
public boolean sendingFailed(MsgQueueEntry[] entries,
XmlBlasterException exception) {
try {
for (int i=0; i<entries.length; i++) {
MsgUnit msgUnit = entries[i].getMsgUnit();
String fn = xbAccess.getFileDumper().dumpMessage(msgUnit.getKeyData(), msgUnit.getContent(), msgUnit.getQosData());
log.severe("Async sending of cluster message failed for " + msgUnit.getKeyOid() +", is dumped to " + fn + ": " + exception.getMessage());
}
}
catch (Throwable e) {
e.printStackTrace();
for (int i=0; i<entries.length; i++)
log.severe("Async sending of message failed for message " + entries[i].toXml() +"\nreason is: " + exception.getMessage());
}
// If PtP send back to sender?
return true; // Remove from connection queue! Now other messages can be delivered
}
});
/*
* // fixed to be unique since 1.5.2 boolean oldQueueNameBehavior =
* this.remoteGlob.getProperty().get(
* "xmlBlaster/cluster/useLegacyClientQueueName", false); if
* (!oldQueueNameBehavior)
* this.xmlBlasterConnection.setStorageIdStr(getId
* ()+connectQosData.getSessionName().getRelativeName()); //now
* setServerNodeId since 1.6.2+
*/
try {
Address addr = connectQosData.getAddress();
log.info("Trying to connect to node '" + getId() + "' on address '" + addr.getRawAddress() + "' using protocol=" + addr.getType());
// TODO: Check if physical IP:PORT is identical
if (this.fatherGlob.getClusterManager().getMyClusterNode().getId().equals(getId())) {
log.severe("We want to connect to ourself, route to node'" + getId() + "' ignored: ConnectQos=" + connectQosData.toXml());
return null;
}
if (log.isLoggable(Level.FINEST)) log.finest("Connecting to other cluster node, ConnectQos=" + connectQosData.toXml());
ConnectQos connectQos = new ConnectQos(this.remoteGlob, connectQosData);
if (useRemoteLoginAsTunnel) {
// We switch off callback ping, it is not yet implemented to handle pings from remote
// We don't need those pings as the other side is responsible to take care on the socket connection