if (log.isLoggable(Level.FINER)) log.finer("Entering " + (publishQos.isClusterUpdate()?"cluster update message ":"") + "publish(oid='" + msgKeyData.getOid() + "', contentMime='" + msgKeyData.getContentMime() + "', contentMimeExtended='" + msgKeyData.getContentMimeExtended() + "' domain='" + msgKeyData.getDomain() + "' from client '" + sessionInfo.getId() + "' ...");
if (log.isLoggable(Level.FINEST)) log.finest("Receiving " + (publishQos.isClusterUpdate()?"cluster update ":"") + " message in publish()\n" + msgUnit.toXml("",80, true) + "\n" + publishQos.toXml() + "\nfrom\n" + sessionInfo.toXml());
PublishReturnQos publishReturnQos = null;
if (! publishQos.isFromPersistenceStore()) {
if (publishQos.getSender() == null) // In cluster routing don't overwrite the original sender
publishQos.setSender(sessionInfo.getSessionName());
if (!myselfLoginName.getLoginName().equals(sessionInfo.getSessionName().getLoginName())) {
// TODO: allow for cluster internal messages?
// TODO: what about different sessions of myselfLoginName?
int hopCount = publishQos.count(glob.getNodeId());
if (hopCount > 0) {
String text = "Warning, message oid='" + msgKeyData.getOid()
+ "' passed my node id='" + glob.getId() + "' " + hopCount + " times before, we have a circular routing problem " +
" mySelf=" + myselfLoginName.getAbsoluteName() + " sessionName=" +
sessionInfo.getSessionName().getAbsoluteName();
if (publishQos.isPtp() && publishQos.getDestinationArr().length > 0) {
text += ", does the destination cluster node '" + publishQos.getDestinationArr()[0].getDestination() + "' exist?";
}
log.warning(text);
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CLUSTER_CIRCULARLOOP, ME, text + " Your QoS:" + publishQos.toXml(""));
}
int stratum = -1; // not known yet, addRouteInfo() sets my stratum to one closer to the master,
// this needs to be checked here as soon as we know which stratum we are!!!
publishQos.addRouteInfo(new RouteInfo(glob.getNodeId(), stratum, publishQos.getRcvTimestamp()));
}
}
this.dispatchStatistic.incrNumPublish(1);
if (msgKeyData.isAdministrative()) {
if (!glob.supportAdministrative())
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_ADMIN_UNAVAILABLE, ME, "Sorry administrative publish() is not available, try to configure xmlBlaster.");
return glob.getMomClientGateway().setCommand(sessionInfo, msgKeyData, msgUnit, publishQos, publishQos.isClusterUpdate());
}
if (msgKeyData.isRemoteProperties()) { // "__sys__remoteProperties"
// Create a remote property on connect and delete again on publish:
// java javaclients.HelloWorldPublish -oid __sys__remoteProperties
// -connect/qos/clientProperty[__remoteProperties] true -connect/qos/clientProperty[myProperty] 1234
// -content clear
String str = msgUnit.getContentStr().trim();
// clear sessionName="" prefix=""
String[] cmdArr = ReplaceVariable.toArray(str, " ");
String command = (cmdArr.length > 0) ? cmdArr[0] : "";
boolean isForOtherClusterNode = false;
SessionName otherSessionName = publishQos.getFirstDestination();
// __sessionName is deprecated, pls use PtP destination to name the session for remoteProperty changes
String __sessionNameStr = publishQos.getData().getClientProperty("__sessionName", (String)null);
if (__sessionNameStr != null) {
otherSessionName = new SessionName(glob, __sessionNameStr);
}
if (glob.useCluster() && otherSessionName != null && otherSessionName.getNodeId() != null && !glob.getNodeId().equals(otherSessionName.getNodeId())) {
isForOtherClusterNode = true; // TODO: Create a PtP destination which routes to node
}
if (!isForOtherClusterNode) {
SessionInfo otherSessionInfo = (otherSessionName == null) ? sessionInfo : getAuthenticate().getSessionInfo(otherSessionName);
if (otherSessionInfo == null) {
if ("clearLastError".equals(command) || "clearLastWarning".equals(command)) {
otherSessionInfo = sessionInfo; // global action, just use the login sessionInf
}
else {
log.warning(msgKeyData.getOid() + " failed, sessionName not known: " + (otherSessionName == null ? "" : otherSessionName.getAbsoluteName()));
return Constants.RET_WARN;
}
}
if (__sessionNameStr != null) publishQos.getData().getClientProperties().remove("__sessionName");
String prefix = publishQos.getData().getClientProperty("__prefix", (String)null);
if (prefix != null) publishQos.getData().getClientProperties().remove("__prefix");
if (log.isLoggable(Level.FINE)) log.fine("Processing " + msgKeyData.getOid() + " command=" + str + " on session=" + otherSessionInfo.getSessionName().getRelativeName());
if ("set".equals(command)) {
otherSessionInfo.setRemoteProperties(publishQos.getData().getClientProperties());
}
else if ("clearLastError".equals(command)) {
clearLastError();
}
else if ("clearLastWarning".equals(command)) {
clearLastWarning();
}
else if ("clear".equals(command)) {
otherSessionInfo.clearRemoteProperties(prefix);
}
else // "merge"
otherSessionInfo.mergeRemoteProperties(publishQos.getData().getClientProperties());
I_RemotePropertiesListener[] arr = getRemotePropertiesListenerArr();
for (int i=0; i<arr.length; i++)
arr[i].update(otherSessionInfo, publishQos.getData().getClientProperties());
return Constants.RET_OK;
}
}
if (msgKeyData.isRunlevelManager()) { // __sys__RunlevelManager
return this.glob.getRunlevelManager().publish(sessionInfo, msgUnit, publishQos);
}
// Check if a publish filter is installed and if so invoke it ...
if (getPublishPluginManager().hasPlugins() && !publishQos.isClusterUpdate()) {
Map mimePlugins = getPublishPluginManager().findMimePlugins(msgKeyData.getContentMime(),msgKeyData.getContentMimeExtended());
if (mimePlugins != null) {
Iterator iterator = mimePlugins.values().iterator();
while (iterator.hasNext()) {
I_PublishFilter plugin = (I_PublishFilter)iterator.next();
if (log.isLoggable(Level.FINE)) log.fine("Message " + msgKeyData.getOid() + " is forwarded to publish plugin");
String ret = plugin.intercept(sessionInfo.getSubjectInfo(), msgUnit);
if (ret == null || ret.length() == 0 || ret.equals(Constants.STATE_OK))
continue;
else {
if (log.isLoggable(Level.FINE)) log.fine("Message " + msgKeyData.getOid() + " is rejected by PublishPlugin");
return "<qos><state id='" + ret + "'/></qos>"; // Message is rejected by PublishPlugin
}
}
}
}
// cluster support - forward pubSub message to master ...
if (this.glob.useCluster()) {
if (!publishQos.isClusterUpdate()) { // updates from other nodes are arriving here in publish as well
//if (!glob.getClusterManager().isReady())
// glob.getClusterManager().blockUntilReady();
if (this.glob.isClusterManagerReady()) {
if (publishQos.isPtp()) { // is PtP message, see req cluster.ptp
Destination[] destinationArr = publishQos.getDestinationArr(); // !!! add XPath client query here !!!
for (int ii = 0; ii<destinationArr.length; ii++) {
if (log.isLoggable(Level.FINE)) log.fine("Working on PtP message for destination [" + destinationArr[ii].getDestination() + "]");
publishReturnQos = forwardPtpPublish(sessionInfo, msgUnit, publishQos.isClusterUpdate(), destinationArr[ii]);
if (publishReturnQos != null) {
if (destinationArr.length > 1) {
// TODO: cluster forwarding with multiple destinations:
String txt = "Messages with more than one destinations in a cluster environment is not implemented, only destination '" + destinationArr[ii].toXml() + "' of '" + msgUnit.getLogId() + "' was delivered";
log.warning(txt);
throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, txt);
}
I_Checkpoint cp = glob.getCheckpointPlugin();
if (cp != null)
cp.passingBy(I_Checkpoint.CP_PUBLISH_ACK, msgUnit, null, null);
return publishReturnQos.toXml();
}
/*
if (publishReturnQos != null) {
// Message was forwarded. TODO: How to return multiple publishReturnQos from multiple destinations? !!!
BUGGY: We need to take a clone to not remove the destination of the sent message
publishQos.removeDestination(destinationArr[ii]);
}
*/
}
/*
if (publishQos.getNumDestinations() == 0) { // we are done, all messages where forwarded
return publishReturnQos.toXml();
}
*/
}
// Publish/Subscribe mode (or if PtP had no result)
else { // if (publishQos.isSubscribable()) {
try {
PublishRetQosWrapper ret = glob.getClusterManager().forwardPublish(sessionInfo, msgUnit);
//Thread.currentThread().dumpStack();
if (ret != null) { // Message was forwarded to master cluster
I_Checkpoint cp = glob.getCheckpointPlugin();
if (cp != null)
cp.passingBy(I_Checkpoint.CP_PUBLISH_ACK, msgUnit, null, null);
publishReturnQos = ret.getPublishReturnQos();
if (ret.getNodeMasterInfo().isDirtyRead() == false) {
if (log.isLoggable(Level.FINE)) log.fine("Message " + msgKeyData.getOid() + " forwarded to master " + ret.getNodeMasterInfo().getId() + ", dirtyRead==false nothing more to do");
return publishReturnQos.toXml();
}
// else we publish it locally as well (dirty read!)
}
}
catch (XmlBlasterException e) {
if (e.getErrorCode() == ErrorCode.RESOURCE_CONFIGURATION_PLUGINFAILED) {
this.glob.setUseCluster(false);
}
else {
e.printStackTrace();
throw e;
}
}
}
}
else {
if (! publishQos.isFromPersistenceStore()) {
if (msgKeyData.isInternal()) {
if (log.isLoggable(Level.FINE)) log.fine("Cluster manager is not ready, handling message '" + msgKeyData.getOid() + "' locally");
}
else {
log.warning("Cluster manager is not ready, handling message '" + msgKeyData.getOid() + "' locally");
}
}
}
}
}
// Handle local message
if (!msgKeyData.getOid().equals(msgUnit.getKeyOid())) {
Thread.dumpStack();
log.severe("Unexpected change of keyOid " + msgKeyData.getOid() + " and msgUnit " + msgUnit.toXml());
}
/*
// Find or create the topic
TopicHandler topicHandler = null;
synchronized(this.topicHandlerMap) {
if (!msgKeyData.getOid().equals(msgUnit.getKeyOid())) {
Thread.dumpStack();
log.severe("Unexpected change of keyOid " + msgKeyData.getOid() + " and msgUnit " + msgUnit.toXml());
}
Object obj = topicHandlerMap.get(msgUnit.getKeyOid());
if (obj == null) {
topicHandler = new TopicHandler(this, sessionInfo, msgUnit.getKeyOid()); // adds itself to topicHandlerMap
}
else {
topicHandler = (TopicHandler)obj;
}
}
*/
TopicHandler topicHandler = null;
try {
topicHandler = this.glob.getTopicAccessor().findOrCreate(sessionInfo, msgUnit.getKeyOid());
// Process the message
publishReturnQos = topicHandler.publish(sessionInfo, msgUnit, publishQos);
}
finally {
this.glob.getTopicAccessor().release(topicHandler);
}
if (publishReturnQos == null) { // assert only
StatusQosData qos = new StatusQosData(glob, MethodName.PUBLISH);
qos.setKeyOid(msgKeyData.getOid());
qos.setState(Constants.STATE_OK);
publishReturnQos = new PublishReturnQos(glob, qos);
publishReturnQos.getData().setRcvTimestamp(publishQos.getRcvTimestamp());
log.severe("Internal: did not excpect to build a PublishReturnQos, but message '" + msgKeyData.getOid() + "' is processed correctly");
Thread.dumpStack();
}
if (!publishQos.isFromPersistenceStore()) {
I_Checkpoint cp = glob.getCheckpointPlugin();
if (cp != null)
cp.passingBy(I_Checkpoint.CP_PUBLISH_ACK, msgUnit, null, null);
}
return publishReturnQos.toXml(); // Use the return value of the cluster master node
}
catch (XmlBlasterException e) {
if (log.isLoggable(Level.FINE)) log.fine("Throwing exception in publish: " + e.toXml()); // Remove again
throw e;
}