Commit c3479bdb authored by Tom Evans's avatar Tom Evans Committed by tevans

OF-205: Added support for Hazelcast-based clustering

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches/pubsub_clustering@13292 b35dd754-fafc-0310-a699-88a17e54d16e
parent ae0536f0
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Hazelcast Clustering Plugin Changelog</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
padding-left : 1em;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
Hazelcast Clustering Plugin Changelog
</h1>
<p><b>1.0.0</b> -- September 10, 2012</p>
<ul>
<li>Initial release. </li>
</ul>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-2.3.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<group>
<name>openfire</name>
<password>openfire</password>
</group>
<management-center enabled="false"/>
<network>
<port auto-increment="true">5701</port>
<join>
<multicast enabled="true">
<multicast-group>224.2.2.3</multicast-group>
<multicast-port>54327</multicast-port>
</multicast>
<tcp-ip enabled="false"/>
<aws enabled="false"/>
</join>
<interfaces enabled="false"/>
<ssl enabled="false" />
<socket-interceptor enabled="false" />
<symmetric-encryption enabled="false">
<!--
encryption algorithm such as
DES/ECB/PKCS5Padding,
PBEWithMD5AndDES,
AES/CBC/PKCS5Padding,
Blowfish,
DESede
-->
<algorithm>PBEWithMD5AndDES</algorithm>
<!-- salt value to use when generating the secret key -->
<salt>thesalt</salt>
<!-- pass phrase to use when generating the secret key -->
<password>thepass</password>
<!-- iteration count to use when generating the secret key -->
<iteration-count>19</iteration-count>
</symmetric-encryption>
<asymmetric-encryption enabled="false">
<!-- encryption algorithm -->
<algorithm>RSA/NONE/PKCS1PADDING</algorithm>
<!-- private key password -->
<keyPassword>thekeypass</keyPassword>
<!-- private key alias -->
<keyAlias>local</keyAlias>
<!-- key store type -->
<storeType>JKS</storeType>
<!-- key store password -->
<storePassword>thestorepass</storePassword>
<!-- path to the key store -->
<storePath>keystore</storePath>
</asymmetric-encryption>
</network>
<partition-group enabled="false"/>
<executor-service>
<core-pool-size>16</core-pool-size>
<max-pool-size>64</max-pool-size>
<keep-alive-seconds>60</keep-alive-seconds>
</executor-service>
<queue name="default">
<!--
Maximum size of the queue. When a JVM's local queue size reaches the maximum,
all put/offer operations will get blocked until the queue size
of the JVM goes down below the maximum.
Any integer between 0 and Integer.MAX_VALUE. 0 means
Integer.MAX_VALUE. Default is 0.
-->
<max-size-per-jvm>0</max-size-per-jvm>
<!--
Name of the map configuration that will be used for the backing distributed
map for this queue.
-->
<backing-map-ref>default</backing-map-ref>
</queue>
<map name="default">
<!--
Number of backups. If 1 is set as the backup-count for example,
then all entries of the map will be copied to another JVM for
fail-safety. 0 means no backup.
-->
<backup-count>1</backup-count>
<!--
Number of async backups. 0 means no backup.
-->
<async-backup-count>0</async-backup-count>
<!--
Maximum number of seconds for each entry to stay in the map. Entries that are
older than <time-to-live-seconds> and not updated for <time-to-live-seconds>
will get automatically evicted from the map.
Any integer between 0 and Integer.MAX_VALUE. 0 means infinite. Default is 0.
-->
<time-to-live-seconds>0</time-to-live-seconds>
<!--
Maximum number of seconds for each entry to stay idle in the map. Entries that are
idle(not touched) for more than <max-idle-seconds> will get
automatically evicted from the map. Entry is touched if get, put or containsKey is called.
Any integer between 0 and Integer.MAX_VALUE. 0 means infinite. Default is 0.
-->
<max-idle-seconds>0</max-idle-seconds>
<!--
Valid values are:
NONE (no eviction),
LRU (Least Recently Used),
LFU (Least Frequently Used).
NONE is the default.
-->
<eviction-policy>NONE</eviction-policy>
<!--
Maximum size of the map. When max size is reached,
map is evicted based on the policy defined.
Any integer between 0 and Integer.MAX_VALUE. 0 means
Integer.MAX_VALUE. Default is 0.
-->
<max-size policy="cluster_wide_map_size">0</max-size>
<!--
When max. size is reached, specified percentage of
the map will be evicted. Any integer between 0 and 100.
If 25 is set for example, 25% of the entries will
get evicted.
-->
<eviction-percentage>25</eviction-percentage>
<!--
While recovering from split-brain (network partitioning),
map entries in the small cluster will merge into the bigger cluster
based on the policy set here. When an entry merge into the
cluster, there might an existing entry with the same key already.
Values of these entries might be different for that same key.
Which value should be set for the key? Conflict is resolved by
the policy set here. Default policy is hz.ADD_NEW_ENTRY
There are built-in merge policies such as
hz.NO_MERGE ; no entry will merge.
hz.ADD_NEW_ENTRY ; entry will be added if the merging entry's key
doesn't exist in the cluster.
hz.HIGHER_HITS ; entry with the higher hits wins.
hz.LATEST_UPDATE ; entry with the latest update wins.
-->
<merge-policy>hz.ADD_NEW_ENTRY</merge-policy>
</map>
</hazelcast>
\ No newline at end of file
After you have licensed and downloaded Coherence EE from Oracle, place
the following jar files in this folder:
coherence.jar
coherence-work.jar
To build the clustering plugin, issue the following command from
the Openfire (source) /build/ folder:
$OPENFIRE_SRC/build> ant -Dplugin=clustering plugin
Also note that due to classpath loading order, it may be necessary to
either remove the coherence-cache-config.xml file from the Coherence
runtime JAR, or rename the plugin-clustering.jar file to force it to
load before coherence.jar (e.g. "clustering-plugin.jar" or similar).
In order to run Oracle Coherence in production mode, you will need to
secure licensing for the Enterprise Edition (EE) of Coherence. While
clustered caching for Openfire is available in the Standard Edition (SE),
per the Oracle Fusion licensing docs the InvocationService (which is
used by Openfire to distribute tasks among the cluster members) is only
available in EE or Grid Edition (GE).
Note that Coherence is configured to run GE in development mode by default.
You can change this setting by overriding the following Java system properties
via /etc/sysconfig/openfire (RPM) or openfired.vmoptions (Windows):
-Dtangosol.coherence.edition=EE
-Dtangosol.coherence.mode=prod
The current Coherence release is version 3.7.1.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>com.jivesoftware.openfire.HazelcastPlugin</class>
<name>${plugin.name}</name>
<description>${plugin.description}</description>
<author>Tom Evans</author>
<version>1.0.0</version>
<date>09/10/2012</date>
<minServerVersion>3.7.2</minServerVersion>
</plugin>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Hazelcast Clustering Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
#datatable TH {
color : #fff;
background-color : #2A448C;
text-align : left;
}
#datatable TD {
background-color : #FAF6EF;
}
#datatable .name {
background-color : #DCE2F5;
}
</style>
</head>
<body>
<h1>
Hazelcast Clustering Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The Hazelcast plugin adds support for running multiple redundant Openfire
servers together in a cluster. By running Openfire in a cluster, you can
distribute the connection load among several servers, as well as having some
form of failover in the event that one of your servers dies. This plugin
is a drop-in replacement for the original Openfire clustering plugin that
removes the dependency on an expensive proprietary third-party product.
</p><p>
The current Hazelcast release is version 2.3.1.</p>
</body>
</html>
##
## Hazelcast Clustering Resource Bundle
##
## REVISION HISTORY
##
## 1.0.0
## Initial Release
plugin.name=Hazelcast Clustering Plugin
plugin.description=Clustering support for Openfire, powered by Hazelcast.
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2004-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire;
import java.io.File;
import java.io.FileFilter;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.jivesoftware.util.cache.ExternalizableUtilStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jivesoftware.openfire.session.RemoteSessionLocator;
import com.jivesoftware.util.cache.ClusterExternalizableUtil;
import com.jivesoftware.util.cluster.ClusterPacketRouter;
/**
* Hazelcast clustering plugin. This implementation is based upon
* (and borrows heavily from) the original Openfire clustering plugin.
*
* @author Tom Evans
* @author Matt Tucker
*/
public class HazelcastPlugin implements Plugin, PropertyEventListener {
private static Logger logger = LoggerFactory.getLogger(HazelcastPlugin.class);
/**
* Keep serialization strategy the server was using before we set our strategy. We will
* restore old strategy when plugin is unloaded.
*/
private ExternalizableUtilStrategy serializationStrategy;
public void initializePlugin(PluginManager manager, File pluginDirectory) {
System.out.println("Starting Clustering Plugin");
// Check if another cluster is installed and stop loading this plugin if found
File pluginDir = new File(JiveGlobals.getHomeDirectory(), "plugins");
File[] jars = pluginDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
String fileName = pathname.getName().toLowerCase();
return (fileName.equalsIgnoreCase("enterprise.jar") ||
fileName.equalsIgnoreCase("coherence.jar"));
}
});
if (jars.length > 0) {
// Do not load this plugin if a conflicting implementation exists
logger.warn("Conflicting clustering plugins found; remove Coherence and/or Enterprise jar files");
throw new IllegalStateException("Clustering plugin configuration conflict (Coherence)");
}
// List for clustering setting events (e.g. enabled/disabled)
PropertyEventDispatcher.addListener(this);
if (ClusterManager.isClusteringEnabled()) {
initForClustering();
// Start up or join the cluster and initialize caches
ClusterManager.startup();
}
}
private void initForClustering() {
// Set the serialization strategy to use for transmitting objects between node clusters
serializationStrategy = ExternalizableUtil.getInstance().getStrategy();
ExternalizableUtil.getInstance().setStrategy(new ClusterExternalizableUtil());
// Set session locator to use when in a cluster
XMPPServer.getInstance().setRemoteSessionLocator(new RemoteSessionLocator());
// Set packet router to use to deliver packets to remote cluster nodes
XMPPServer.getInstance().getRoutingTable().setRemotePacketRouter(new ClusterPacketRouter());
}
/**
* Returns the date when this release of Openfire clustering plugin was released.
*
* @return the date when this release of Openfire clustering plugin was released.
*/
public static Date getReleaseDate() {
try {
// @DATE@ should be replaced with a date with the following format: Jan 31, 2007
// Automatically set by ANT build tasks
return DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US).parse("@DATE@");
}
catch (ParseException e) {
logger.error("Error parsing date", e);
return null;
}
}
public void destroyPlugin() {
ClusterManager.shutdown();
// Set the old serialization strategy was using before clustering was loaded
ExternalizableUtil.getInstance().setStrategy(serializationStrategy);
// Stop listing for clustering setting events (e.g. enabled/disabled)
PropertyEventDispatcher.removeListener(this);
}
public void propertySet(String property, Map<String, Object> params) {
// Ignore
}
public void propertyDeleted(String property, Map<String, Object> params) {
// Ignore
}
public void xmlPropertySet(String property, Map<String, Object> params) {
if (ClusterManager.CLUSTER_PROPERTY_NAME.equals(property)) {
if (Boolean.parseBoolean((String) params.get("value"))) {
// Clustering was enabled
initForClustering();
}
else {
// Clustering was disabled
}
}
}
public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// Do nothing
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.spi.ClientRoute;
import org.jivesoftware.openfire.spi.RoutingTableImpl;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Class that defines possible remote operations that could be performed
* on remote client sessions.
*
* @author Gaston Dombiak
*/
public class ClientSessionTask extends RemoteSessionTask {
private JID address;
public ClientSessionTask() {
super();
}
protected ClientSessionTask(JID address, Operation operation) {
super(operation);
this.address = address;
}
Session getSession() {
return XMPPServer.getInstance().getRoutingTable().getClientRoute(address);
}
public void run() {
super.run();
ClientSession session = (ClientSession) getSession();
if (session instanceof RemoteClientSession) {
// The session is being hosted by other cluster node so log this unexpected case
Cache<String, ClientRoute> usersCache = CacheFactory.createCache(RoutingTableImpl.C2S_CACHE_NAME);
ClientRoute route = usersCache.get(address.toString());
NodeID nodeID = route.getNodeID();
Log.warn("Found remote session instead of local session. JID: " + address + " found in Node: " +
nodeID.toByteArray() + " and local node is: " + XMPPServer.getInstance().getNodeID().toByteArray());
}
if (operation == Operation.isInitialized) {
if (session instanceof RemoteClientSession) {
// Something is wrong since the session shoud be local instead of remote
// Assume some default value
result = true;
}
else {
result = session.isInitialized();
}
}
else if (operation == Operation.incrementConflictCount) {
if (session instanceof RemoteClientSession) {
// Something is wrong since the session shoud be local instead of remote
// Assume some default value
result = 2;
}
else {
result = session.incrementConflictCount();
}
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, address.toString());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
address = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
public String toString() {
return super.toString() + " operation: " + operation + " address: " + address;
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.session.ComponentSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Class that defines possible remote operations that could be performed
* on remote component sessions (for external components only).
*
* @author Gaston Dombiak
*/
public class ComponentSessionTask extends RemoteSessionTask {
private JID address;
public ComponentSessionTask() {
}
protected ComponentSessionTask(JID address, Operation operation) {
super(operation);
this.address = address;
}
Session getSession() {
return SessionManager.getInstance().getComponentSession(address.getDomain());
}
public void run() {
super.run();
if (operation == Operation.getType) {
result = ((ComponentSession) getSession()).getExternalComponent().getType();
}
else if (operation == Operation.getCategory) {
result = ((ComponentSession) getSession()).getExternalComponent().getCategory();
}
else if (operation == Operation.getInitialSubdomain) {
result = ((ComponentSession) getSession()).getExternalComponent().getInitialSubdomain();
}
else if (operation == Operation.getSubdomains) {
result = ((ComponentSession) getSession()).getExternalComponent().getSubdomains();
}
else if (operation == Operation.getName) {
result = ((ComponentSession) getSession()).getExternalComponent().getName();
}
else if (operation == Operation.getDescription) {
result = ((ComponentSession) getSession()).getExternalComponent().getDescription();
}
else if (operation == Operation.start) {
((ComponentSession) getSession()).getExternalComponent().start();
}
else if (operation == Operation.shutdown) {
((ComponentSession) getSession()).getExternalComponent().shutdown();
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, address.toString());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
address = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
public String toString() {
return super.toString() + " operation: " + operation + " address: " + address;
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.session.Session;
import org.xmpp.packet.JID;
/**
* Class that defines possible remote operations that could be performed
* on remote connection manager sessions.
*
* @author Gaston Dombiak
*/
public class ConnectionMultiplexerSessionTask extends RemoteSessionTask {
private JID address;
public ConnectionMultiplexerSessionTask() {
}
protected ConnectionMultiplexerSessionTask(JID address, Operation operation) {
super(operation);
this.address = address;
}
Session getSession() {
return SessionManager.getInstance().getConnectionMultiplexerSession(address);
}
public String toString() {
return super.toString() + " operation: " + operation + " address: " + address;
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Cluster task that will ask a remote cluster node to deliver some raw text to a local session.
*
* @author Gaston Dombiak
*/
public class DeliverRawTextTask implements ClusterTask {
private SessionType sessionType;
private JID address;
private String streamID;
private String text;
public DeliverRawTextTask() {
super();
}
protected DeliverRawTextTask(RemoteSession remoteSession, JID address, String text) {
if (remoteSession instanceof RemoteClientSession) {
this.sessionType = SessionType.client;
}
else if (remoteSession instanceof RemoteOutgoingServerSession) {
this.sessionType = SessionType.outgoingServer;
}
else if (remoteSession instanceof RemoteComponentSession) {
this.sessionType = SessionType.component;
}
else if (remoteSession instanceof RemoteConnectionMultiplexerSession) {
this.sessionType = SessionType.connectionManager;
}
else {
Log.error("Invalid RemoteSession was used for task: " + remoteSession);
}
this.address = address;
this.text = text;
}
public DeliverRawTextTask(String streamID, String text) {
this.sessionType = SessionType.incomingServer;
this.streamID = streamID;
this.text = text;
}
public Object getResult() {
return null;
}
public void run() {
getSession().deliverRawText(text);
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSafeUTF(out, text);
ExternalizableUtil.getInstance().writeInt(out, sessionType.ordinal());
ExternalizableUtil.getInstance().writeBoolean(out, address != null);
if (address != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, address.toString());
}
ExternalizableUtil.getInstance().writeBoolean(out, streamID != null);
if (streamID != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, streamID);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
text = ExternalizableUtil.getInstance().readSafeUTF(in);
sessionType = SessionType.values()[ExternalizableUtil.getInstance().readInt(in)];
if (ExternalizableUtil.getInstance().readBoolean(in)) {
address = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
if (ExternalizableUtil.getInstance().readBoolean(in)) {
streamID = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
Session getSession() {
if (sessionType == SessionType.client) {
return XMPPServer.getInstance().getRoutingTable().getClientRoute(address);
}
else if (sessionType == SessionType.component) {
return SessionManager.getInstance().getComponentSession(address.getDomain());
}
else if (sessionType == SessionType.connectionManager) {
return SessionManager.getInstance().getConnectionMultiplexerSession(address);
}
else if (sessionType == SessionType.outgoingServer) {
return SessionManager.getInstance().getOutgoingServerSession(address.getDomain());
}
else if (sessionType == SessionType.incomingServer) {
return SessionManager.getInstance().getIncomingServerSession(streamID);
}
Log.error("Found unknown session type: " + sessionType);
return null;
}
public String toString() {
return super.toString() + " sessionType: " + sessionType + " address: " + address;
}
private enum SessionType {
client,
outgoingServer,
incomingServer,
component,
connectionManager
}
}
\ No newline at end of file
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.session.IncomingServerSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.cache.ExternalizableUtil;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Class that defines possible remote operations that could be performed
* on remote incoming server sessions.
*
* @author Gaston Dombiak
*/
public class IncomingServerSessionTask extends RemoteSessionTask {
private String streamID;
public IncomingServerSessionTask() {
super();
}
protected IncomingServerSessionTask(Operation operation, String streamID) {
super(operation);
this.streamID = streamID;
}
Session getSession() {
return SessionManager.getInstance().getIncomingServerSession(streamID);
}
public void run() {
super.run();
if (operation == Operation.getLocalDomain) {
result = ((IncomingServerSession) getSession()).getLocalDomain();
}
else if (operation == Operation.getAddress) {
result = getSession().getAddress();
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, streamID);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
streamID = ExternalizableUtil.getInstance().readSafeUTF(in);
}
public String toString() {
return super.toString() + " operation: " + operation + " streamID: " + streamID;
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.session.OutgoingServerSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Class that defines possible remote operations that could be performed
* on remote outgoing server sessions.
*
* @author Gaston Dombiak
*/
public class OutgoingServerSessionTask extends RemoteSessionTask {
private JID address;
public OutgoingServerSessionTask() {
}
protected OutgoingServerSessionTask(JID address, Operation operation) {
super(operation);
this.address = address;
}
Session getSession() {
return SessionManager.getInstance().getOutgoingServerSession(address.getDomain());
}
public void run() {
super.run();
if (operation == Operation.getAuthenticatedDomains) {
result = ((OutgoingServerSession) getSession()).getAuthenticatedDomains();
}
else if (operation == Operation.getHostnames) {
result = ((OutgoingServerSession) getSession()).getHostnames();
}
else if (operation == Operation.isUsingServerDialback) {
result = ((OutgoingServerSession) getSession()).isUsingServerDialback();
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, address.toString());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
address = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
public String toString() {
return super.toString() + " operation: " + operation + " address: " + address;
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Cluster task that will ask a remote cluster node to deliver some packet to a local session.
*
* @author Gaston Dombiak
*/
public class ProcessPacketTask implements ClusterTask {
private SessionType sessionType;
private JID address;
private String streamID;
private Packet packet;
public ProcessPacketTask() {
super();
}
protected ProcessPacketTask(RemoteSession remoteSession, JID address, Packet packet) {
if (remoteSession instanceof RemoteClientSession) {
this.sessionType = SessionType.client;
}
else if (remoteSession instanceof RemoteOutgoingServerSession) {
this.sessionType = SessionType.outgoingServer;
}
else if (remoteSession instanceof RemoteComponentSession) {
this.sessionType = SessionType.component;
}
else if (remoteSession instanceof RemoteConnectionMultiplexerSession) {
this.sessionType = SessionType.connectionManager;
}
else {
Log.error("Invalid RemoteSession was used for task: " + remoteSession);
}
this.address = address;
this.packet = packet;
}
protected ProcessPacketTask(String streamID, Packet packet) {
this.sessionType = SessionType.incomingServer;
this.streamID = streamID;
this.packet = packet;
}
public Object getResult() {
return null;
}
public void run() {
getSession().process(packet);
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeBoolean(out, address != null);
if (address != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, address.toString());
}
ExternalizableUtil.getInstance().writeBoolean(out, streamID != null);
if (streamID != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, streamID);
}
ExternalizableUtil.getInstance().writeInt(out, sessionType.ordinal());
if (packet instanceof IQ) {
ExternalizableUtil.getInstance().writeInt(out, 1);
} else if (packet instanceof Message) {
ExternalizableUtil.getInstance().writeInt(out, 2);
} else if (packet instanceof Presence) {
ExternalizableUtil.getInstance().writeInt(out, 3);
}
ExternalizableUtil.getInstance().writeSerializable(out, (DefaultElement) packet.getElement());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
if (ExternalizableUtil.getInstance().readBoolean(in)) {
address = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
if (ExternalizableUtil.getInstance().readBoolean(in)) {
streamID = ExternalizableUtil.getInstance().readSafeUTF(in);
}
sessionType = SessionType.values()[ExternalizableUtil.getInstance().readInt(in)];
int packetType = ExternalizableUtil.getInstance().readInt(in);
Element packetElement = (Element) ExternalizableUtil.getInstance().readSerializable(in);
switch (packetType) {
case 1:
packet = new IQ(packetElement, true);
break;
case 2:
packet = new Message(packetElement, true);
break;
case 3:
packet = new Presence(packetElement, true);
break;
}
}
Session getSession() {
if (sessionType == SessionType.client) {
return XMPPServer.getInstance().getRoutingTable().getClientRoute(address);
}
else if (sessionType == SessionType.component) {
return SessionManager.getInstance().getComponentSession(address.getDomain());
}
else if (sessionType == SessionType.connectionManager) {
return SessionManager.getInstance().getConnectionMultiplexerSession(address);
}
else if (sessionType == SessionType.outgoingServer) {
return SessionManager.getInstance().getOutgoingServerSession(address.getDomain());
}
else if (sessionType == SessionType.incomingServer) {
return SessionManager.getInstance().getIncomingServerSession(streamID);
}
Log.error("Found unknown session type: " + sessionType);
return null;
}
public String toString() {
return super.toString() + " sessionType: " + sessionType + " address: " + address;
}
private enum SessionType {
client,
outgoingServer,
incomingServer,
component,
connectionManager
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.session.ComponentSession;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.packet.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
/**
* Surrogate for sessions of external components hosted in some remote cluster node.
*
* @author Gaston Dombiak
*/
public class RemoteComponentSession extends RemoteSession implements ComponentSession {
private ExternalComponent component;
public RemoteComponentSession(byte[] nodeID, JID address) {
super(nodeID, address);
component = new RemoteExternalComponent(address);
}
public ExternalComponent getExternalComponent() {
return component;
}
RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation) {
return new ComponentSessionTask(address, operation);
}
ClusterTask getDeliverRawTextTask(String text) {
return new DeliverRawTextTask(this, address, text);
}
ClusterTask getProcessPacketTask(Packet packet) {
return new ProcessPacketTask(this, address, packet);
}
private class RemoteExternalComponent implements ExternalComponent {
private JID address;
public RemoteExternalComponent(JID address) {
this.address = address;
}
public void setName(String name) {
RemoteSessionTask task = new SetterTask(address, SetterTask.Type.name, name);
doClusterTask(task);
}
public String getType() {
ClusterTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.getType);
return (String) doSynchronousClusterTask(task);
}
public void setType(String type) {
RemoteSessionTask task = new SetterTask(address, SetterTask.Type.type, type);
doClusterTask(task);
}
public String getCategory() {
ClusterTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.getCategory);
return (String) doSynchronousClusterTask(task);
}
public void setCategory(String category) {
RemoteSessionTask task = new SetterTask(address, SetterTask.Type.catergory, category);
doClusterTask(task);
}
public String getInitialSubdomain() {
ClusterTask task =
new ComponentSessionTask(address, RemoteSessionTask.Operation.getInitialSubdomain);
return (String) doSynchronousClusterTask(task);
}
public Collection<String> getSubdomains() {
ClusterTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.getSubdomains);
return (Collection<String>) doSynchronousClusterTask(task);
}
public String getName() {
ClusterTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.getName);
return (String) doSynchronousClusterTask(task);
}
public String getDescription() {
ClusterTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.getDescription);
return (String) doSynchronousClusterTask(task);
}
public void processPacket(Packet packet) {
RemoteSessionTask task = new ProcessComponentPacketTask(address, packet);
doClusterTask(task);
}
public void initialize(JID jid, ComponentManager componentManager) throws ComponentException {
RemoteSessionTask task = new InitializeTask(address, jid);
doClusterTask(task);
}
public void start() {
RemoteSessionTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.start);
doClusterTask(task);
}
public void shutdown() {
RemoteSessionTask task = new ComponentSessionTask(address, RemoteSessionTask.Operation.shutdown);
doClusterTask(task);
}
}
private static class SetterTask extends ComponentSessionTask {
private Type type;
private String value;
public SetterTask() {
super();
}
protected SetterTask(JID address, Type type, String value) {
super(address, null);
this.type = type;
this.value = value;
}
public void run() {
if (type == Type.name) {
((ComponentSession) getSession()).getExternalComponent().setName(value);
} else if (type == Type.type) {
((ComponentSession) getSession()).getExternalComponent().setType(value);
} else if (type == Type.catergory) {
((ComponentSession) getSession()).getExternalComponent().setCategory(value);
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeInt(out, type.ordinal());
ExternalizableUtil.getInstance().writeBoolean(out, value != null);
if (value != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, value);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
type = Type.values()[ExternalizableUtil.getInstance().readInt(in)];
if (ExternalizableUtil.getInstance().readBoolean(in)) {
value = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
private static enum Type {
name,
type,
catergory
}
}
private static class ProcessComponentPacketTask extends ComponentSessionTask {
private Packet packet;
public ProcessComponentPacketTask() {
super();
}
protected ProcessComponentPacketTask(JID address, Packet packet) {
super(address, null);
this.packet = packet;
}
public void run() {
((ComponentSession) getSession()).getExternalComponent().processPacket(packet);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
if (packet instanceof IQ) {
ExternalizableUtil.getInstance().writeInt(out, 1);
} else if (packet instanceof Message) {
ExternalizableUtil.getInstance().writeInt(out, 2);
} else if (packet instanceof Presence) {
ExternalizableUtil.getInstance().writeInt(out, 3);
}
ExternalizableUtil.getInstance().writeSerializable(out, (DefaultElement) packet.getElement());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
int packetType = ExternalizableUtil.getInstance().readInt(in);
Element packetElement = (Element) ExternalizableUtil.getInstance().readSerializable(in);
switch (packetType) {
case 1:
packet = new IQ(packetElement, true);
break;
case 2:
packet = new Message(packetElement, true);
break;
case 3:
packet = new Presence(packetElement, true);
break;
}
}
}
private static class InitializeTask extends ComponentSessionTask {
private JID componentJID;
public InitializeTask() {
super();
}
protected InitializeTask(JID address, JID componentJID) {
super(address, null);
this.componentJID = componentJID;
}
public void run() {
try {
((ComponentSession) getSession()).getExternalComponent()
.initialize(componentJID, InternalComponentManager.getInstance());
} catch (ComponentException e) {
Log.error("Error initializing component", e);
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, componentJID.toString());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
componentJID = new JID(ExternalizableUtil.getInstance().readSafeUTF(in));
}
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.session.ConnectionMultiplexerSession;
import org.jivesoftware.util.cache.ClusterTask;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
/**
* Surrogate for connection manager sessions hosted in some remote cluster node.
*
* @author Gaston Dombiak
*/
public class RemoteConnectionMultiplexerSession extends RemoteSession implements ConnectionMultiplexerSession {
public RemoteConnectionMultiplexerSession(byte[] nodeID, JID address) {
super(nodeID, address);
}
RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation) {
return new ConnectionMultiplexerSessionTask(address, operation);
}
ClusterTask getDeliverRawTextTask(String text) {
return new DeliverRawTextTask(this, address, text);
}
ClusterTask getProcessPacketTask(Packet packet) {
return new ProcessPacketTask(this, address, packet);
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.session.IncomingServerSession;
import org.jivesoftware.util.cache.ClusterTask;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import java.util.Collection;
/**
* Surrogate for incoming server sessions hosted in some remote cluster node.
*
* @author Gaston Dombiak
*/
public class RemoteIncomingServerSession extends RemoteSession implements IncomingServerSession {
private String localDomain;
public RemoteIncomingServerSession(byte[] nodeID, String streamID) {
super(nodeID, null);
this.streamID = new BasicStreamID(streamID);
}
public JID getAddress() {
if (address == null) {
RemoteSessionTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getAddress);
address = (JID) doSynchronousClusterTask(task);
}
return address;
}
public Collection<String> getValidatedDomains() {
// Content is stored in a clustered cache so that even in the case of the node hosting
// the sessions is lost we can still have access to this info to be able to perform
// proper clean up logic {@link ClusterListener#cleanupNode(NodeCacheKey)
return SessionManager.getInstance().getValidatedDomains(streamID.getID());
}
public String getLocalDomain() {
if (localDomain == null) {
RemoteSessionTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getLocalDomain);
localDomain = (String) doSynchronousClusterTask(task);
}
return localDomain;
}
RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation) {
return new IncomingServerSessionTask(operation, streamID.getID());
}
ClusterTask getDeliverRawTextTask(String text) {
return new DeliverRawTextTask(streamID.getID(), text);
}
ClusterTask getProcessPacketTask(Packet packet) {
return new ProcessPacketTask(streamID.getID(), packet);
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.session.OutgoingServerSession;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
/**
* Surrogate for outgoing server sessions hosted in some remote cluster node.
*
* @author Gaston Dombiak
*/
public class RemoteOutgoingServerSession extends RemoteSession implements OutgoingServerSession {
private long usingServerDialback = -1;
public RemoteOutgoingServerSession(byte[] nodeID, JID address) {
super(nodeID, address);
}
public Collection<String> getAuthenticatedDomains() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getAuthenticatedDomains);
return (Collection<String>) doSynchronousClusterTask(task);
}
public void addAuthenticatedDomain(String domain) {
doClusterTask(new AddAuthenticatedDomainTask(address, domain));
}
public Collection<String> getHostnames() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getHostnames);
return (Collection<String>) doSynchronousClusterTask(task);
}
public void addHostname(String hostname) {
doClusterTask(new AddHostnameTask(address, hostname));
}
public boolean authenticateSubdomain(String domain, String hostname) {
ClusterTask task = new AuthenticateSubdomainTask(address, domain, hostname);
return (Boolean) doSynchronousClusterTask(task);
}
public boolean isUsingServerDialback() {
if (usingServerDialback == -1) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.isUsingServerDialback);
usingServerDialback = (Boolean) doSynchronousClusterTask(task) ? 1 : 0;
}
return usingServerDialback == 1;
}
RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation) {
return new OutgoingServerSessionTask(address, operation);
}
ClusterTask getDeliverRawTextTask(String text) {
return new DeliverRawTextTask(this, address, text);
}
ClusterTask getProcessPacketTask(Packet packet) {
return new ProcessPacketTask(this, address, packet);
}
private static class AddAuthenticatedDomainTask extends OutgoingServerSessionTask {
private String domain;
public AddAuthenticatedDomainTask() {
super();
}
protected AddAuthenticatedDomainTask(JID address, String domain) {
super(address, null);
this.domain = domain;
}
public void run() {
((OutgoingServerSession) getSession()).addAuthenticatedDomain(domain);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, domain);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
domain = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
private static class AddHostnameTask extends OutgoingServerSessionTask {
private String hostname;
public AddHostnameTask() {
super();
}
protected AddHostnameTask(JID address, String hostname) {
super(address, null);
this.hostname = hostname;
}
public void run() {
((OutgoingServerSession) getSession()).addHostname(hostname);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, hostname);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
hostname = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
private static class AuthenticateSubdomainTask extends OutgoingServerSessionTask {
private String domain;
private String hostname;
public AuthenticateSubdomainTask() {
super();
}
protected AuthenticateSubdomainTask(JID address, String domain, String hostname) {
super(address, null);
this.domain = domain;
this.hostname = hostname;
}
public void run() {
result = ((OutgoingServerSession) getSession()).authenticateSubdomain(domain, hostname);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, domain);
ExternalizableUtil.getInstance().writeSafeUTF(out, hostname);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
domain = ExternalizableUtil.getInstance().readSafeUTF(in);
hostname = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.cache.CacheFactory;
import org.jivesoftware.util.cache.ClusterTask;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import java.net.UnknownHostException;
import java.util.Date;
/**
* Base class for sessions being hosted in other cluster nodes. Almost all
* messages will be forwarded to the actual session in some remote cluster node.
* Only some few messages will be local operations like getting the session's address
* or the session status. And only some operations will be cached locally for a brief
* period for content that is highly used and not frequently modified.
*
* @author Gaston Dombiak
*/
public abstract class RemoteSession implements Session {
protected byte[] nodeID;
protected JID address;
// Cache content that never changes
protected StreamID streamID;
private Date creationDate;
private String serverName;
private String hostAddress;
private String hostName;
public RemoteSession(byte[] nodeID, JID address) {
this.nodeID = nodeID;
this.address = address;
}
public JID getAddress() {
return address;
}
/**
* Remote sessions are always authenticated. Otherwise, they won't be visibile to other
* cluster nodes. When the session is closed it will no longer be visible to other nodes
* so {@link #STATUS_CLOSED} is never returned.
*
* @return the authenticated status.
*/
public int getStatus() {
return STATUS_AUTHENTICATED;
}
public StreamID getStreamID() {
// Get it once and cache it since it never changes
if (streamID == null) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getStreamID);
String id = (String) doSynchronousClusterTask(task);
streamID = new BasicStreamID(id);
}
return streamID;
}
public String getServerName() {
if (serverName == null) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getServerName);
serverName = (String) doSynchronousClusterTask(task);
}
return serverName;
}
public Date getCreationDate() {
// Get it once and cache it since it never changes
if (creationDate == null) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getCreationDate);
creationDate = (Date) doSynchronousClusterTask(task);
}
return creationDate;
}
public Date getLastActiveDate() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getLastActiveDate);
return (Date) doSynchronousClusterTask(task);
}
public long getNumClientPackets() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getNumClientPackets);
return (Long) doSynchronousClusterTask(task);
}
public long getNumServerPackets() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getNumServerPackets);
return (Long) doSynchronousClusterTask(task);
}
public void process(Packet packet) {
doClusterTask(getProcessPacketTask(packet));
}
public void close() {
doSynchronousClusterTask(getRemoteSessionTask(RemoteSessionTask.Operation.close));
}
public boolean isClosed() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.isClosed);
return (Boolean) doSynchronousClusterTask(task);
}
public boolean isSecure() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.isSecure);
return (Boolean) doSynchronousClusterTask(task);
}
public String getHostAddress() throws UnknownHostException {
if (hostAddress == null) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getHostAddress);
hostAddress = (String) doSynchronousClusterTask(task);
}
return hostAddress;
}
public String getHostName() throws UnknownHostException {
if (hostName == null) {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.getHostName);
hostName = (String) doSynchronousClusterTask(task);
}
return hostName;
}
public void deliverRawText(String text) {
doClusterTask(getDeliverRawTextTask(text));
}
public boolean validate() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.validate);
return (Boolean) doSynchronousClusterTask(task);
}
abstract RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation);
abstract ClusterTask getDeliverRawTextTask(String text);
abstract ClusterTask getProcessPacketTask(Packet packet);
/**
* Invokes a task on the remote cluster member synchronously and returns the result of
* the remote operation.
*
* @param task the ClusterTask object to be invoked on a given cluster member.
* @return result of remote operation.
* @throws IllegalStateException if requested node was not found or not running in a cluster.
*/
protected Object doSynchronousClusterTask(ClusterTask task) {
return CacheFactory.doSynchronousClusterTask(task, nodeID);
}
/**
* Invokes a task on the remote cluster member in an asynchronous fashion.
*
* @param task the task to be invoked on the specified cluster member.
* @throws IllegalStateException if requested node was not found or not running in a cluster.
*/
protected void doClusterTask(ClusterTask task) {
CacheFactory.doClusterTask(task, nodeID);
}
/**
* Simple implementation of the StreamID interface to hold the stream ID of
* the surrogated session.
*/
protected static class BasicStreamID implements StreamID {
String id;
public BasicStreamID(String id) {
this.id = id;
}
public String getID() {
return id;
}
public String toString() {
return id;
}
public int hashCode() {
return id.hashCode();
}
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.session.*;
import org.xmpp.packet.JID;
/**
* Locator of sessions that know how to talk to Coherence cluster nodes.
*
* @author Gaston Dombiak
*/
public class RemoteSessionLocator implements org.jivesoftware.openfire.session.RemoteSessionLocator {
// TODO Keep a cache for a brief moment so we can reuse same instances (that use their own cache)
public ClientSession getClientSession(byte[] nodeID, JID address) {
return new RemoteClientSession(nodeID, address);
}
public ComponentSession getComponentSession(byte[] nodeID, JID address) {
return new RemoteComponentSession(nodeID, address);
}
public ConnectionMultiplexerSession getConnectionMultiplexerSession(byte[] nodeID, JID address) {
return new RemoteConnectionMultiplexerSession(nodeID, address);
}
public IncomingServerSession getIncomingServerSession(byte[] nodeID, String streamID) {
return new RemoteIncomingServerSession(nodeID, streamID);
}
public OutgoingServerSession getOutgoingServerSession(byte[] nodeID, JID address) {
return new RemoteOutgoingServerSession(nodeID, address);
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.openfire.session;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.TaskEngine;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.net.UnknownHostException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* Operations to be executed in a remote session hosted in a remote cluster node.
*
* @author Gaston Dombiak
*/
public abstract class RemoteSessionTask implements ClusterTask {
protected Object result;
protected Operation operation;
public RemoteSessionTask() {
}
protected RemoteSessionTask(Operation operation) {
this.operation = operation;
}
abstract Session getSession();
public Object getResult() {
return result;
}
public void run() {
if (operation == Operation.getStreamID) {
result = getSession().getStreamID().getID();
}
else if (operation == Operation.getServerName) {
result = getSession().getServerName();
}
else if (operation == Operation.getCreationDate) {
result = getSession().getCreationDate();
}
else if (operation == Operation.getLastActiveDate) {
result = getSession().getLastActiveDate();
}
else if (operation == Operation.getNumClientPackets) {
result = getSession().getNumClientPackets();
}
else if (operation == Operation.getNumServerPackets) {
result = getSession().getNumServerPackets();
}
else if (operation == Operation.close) {
// Run in another thread so we avoid blocking calls (in coherence)
final Session session = getSession();
if (session != null) {
final Future<?> future = TaskEngine.getInstance().submit(new Runnable() {
public void run() {
session.close();
}
});
// Wait until the close operation is done or timeout is met
try {
future.get(15, TimeUnit.SECONDS);
}
catch (Exception e) {
// Ignore
}
}
}
else if (operation == Operation.isClosed) {
result = getSession().isClosed();
}
else if (operation == Operation.isSecure) {
result = getSession().isSecure();
}
else if (operation == Operation.getHostAddress) {
try {
result = getSession().getHostAddress();
} catch (UnknownHostException e) {
Log.error("Error getting address of session: " + getSession(), e);
}
}
else if (operation == Operation.getHostName) {
try {
result = getSession().getHostName();
} catch (UnknownHostException e) {
Log.error("Error getting address of session: " + getSession(), e);
}
}
else if (operation == Operation.validate) {
result = getSession().validate();
}
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeBoolean(out, operation != null);
if (operation != null) {
ExternalizableUtil.getInstance().writeInt(out, operation.ordinal());
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
if (ExternalizableUtil.getInstance().readBoolean(in)) {
operation = Operation.values()[ExternalizableUtil.getInstance().readInt(in)];
}
}
public enum Operation {
/**
* Basic session operations
*/
getStreamID,
getServerName,
getCreationDate,
getLastActiveDate,
getNumClientPackets,
getNumServerPackets,
close,
isClosed,
isSecure,
getHostAddress,
getHostName,
validate,
/**
* Operations of c2s sessions
*/
isInitialized,
incrementConflictCount,
/**
* Operations of outgoing server sessions
*/
getAuthenticatedDomains,
getHostnames,
isUsingServerDialback,
/**
* Operations of external component sessions
*/
getType,
getCategory,
getInitialSubdomain,
getSubdomains,
getName,
getDescription,
start,
shutdown,
/**
* Operations of incoming server sessions
*/
getLocalDomain,
getAddress
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cache;
import java.util.Set;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.NodeID;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
/**
* Base listener for cache events in the cluster. This class helps keep track
* of nodes and their elements. The actual tracking information is kept in
* {@link ClusterListener}. This information is then used when a node goes
* down to proper clean up can be done.
*
* @author Tom Evans
* @author Pete Matern
* @author Gaston Dombiak
*/
class CacheListener implements EntryListener {
protected final String cacheName;
private ClusterListener clusterListener;
public CacheListener(ClusterListener clusterListener, String cacheName) {
this.clusterListener = clusterListener;
this.cacheName = cacheName;
}
@Override
public void entryAdded(EntryEvent event) {
handleMapEvent(event, false);
}
@Override
public void entryUpdated(EntryEvent event) {
handleMapEvent(event, false);
}
@Override
public void entryRemoved(EntryEvent event) {
handleMapEvent(event, true);
}
@Override
public void entryEvicted(EntryEvent event) {
handleMapEvent(event, true);
}
void handleMapEvent(EntryEvent event, boolean removal) {
NodeID nodeID = NodeID.getInstance(event.getMember().getUuid().getBytes());
//ignore items which this node has added
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
Set<String> sessionJIDS = clusterListener.lookupJIDList(nodeID, cacheName);
if (removal) {
sessionJIDS.remove(event.getKey().toString());
}
else {
sessionJIDS.add(event.getKey().toString());
}
}
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cache;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginClassLoader;
import org.jivesoftware.openfire.container.PluginManager;
/**
* Class loader to be used by Openfire to load classes that live in the Hazelcast plugin,
* the Openfire core and also classes defined in other plugins. With this new class loader
* plugins can now make use of hazelcast.<p>
*
* However, there is a catch with this class loader. Plugins that define the same class name
* (i.e. package and class name) will have a problem if they try to send that class through
* the cluster. Hazelcast will deserialize the class and will use the first class definition
* found in the list of plugins.<p>
*
* The sequence of search for this class loader is first check the hazelcast plugin that
* includes checking the Openfire core. If not found then try with the other plugins.
*
* @author Tom Evans
* @author Gaston Dombiak
*/
public class ClusterClassLoader extends ClassLoader {
private PluginClassLoader hazelcastClassloader;
public ClusterClassLoader() {
super();
Plugin plugin = XMPPServer.getInstance().getPluginManager().getPlugin("hazelcast");
hazelcastClassloader = XMPPServer.getInstance().getPluginManager().getPluginClassloader(plugin);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
return hazelcastClassloader.loadClass(name);
}
catch (ClassNotFoundException e) {
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
for (Plugin plugin : pluginManager.getPlugins()) {
String pluginName = pluginManager.getPluginDirectory(plugin).getName();
if ("hazelcast".equals(pluginName) || "admin".equals(pluginName)) {
continue;
}
PluginClassLoader pluginClassloader = pluginManager.getPluginClassloader(plugin);
try {
return pluginClassloader.loadClass(name);
}
catch (ClassNotFoundException e1) {
// Do nothing. Continue to the next plugin
}
}
}
throw new ClassNotFoundException(name);
}
public URL getResource(String name) {
URL resource = hazelcastClassloader.getResource(name);
if (resource == null) {
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
for (Plugin plugin : pluginManager.getPlugins()) {
String pluginName = pluginManager.getPluginDirectory(plugin).getName();
if ("hazelcast".equals(pluginName) || "admin".equals(pluginName)) {
continue;
}
PluginClassLoader pluginClassloader = pluginManager.getPluginClassloader(plugin);
resource = pluginClassloader.getResource(name);
if (resource != null) {
return resource;
}
}
}
return resource;
}
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration<URL> answer = null;
try {
answer = hazelcastClassloader.getResources(name);
}
catch (IOException e) {
// Ignore
}
if (answer == null || !answer.hasMoreElements()) {
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
for (Plugin plugin : pluginManager.getPlugins()) {
String pluginName = pluginManager.getPluginDirectory(plugin).getName();
if ("hazelcast".equals(pluginName) || "admin".equals(pluginName)) {
continue;
}
PluginClassLoader pluginClassloader = pluginManager.getPluginClassloader(plugin);
try {
answer = pluginClassloader.getResources(name);
}
catch (IOException e) {
// Ignore
}
if (answer != null && answer.hasMoreElements()) {
return answer;
}
}
}
return answer;
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cache;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.core.EntryListener;
import com.hazelcast.core.IMap;
import com.hazelcast.monitor.LocalMapStats;
/**
* Clustered implementation of the Cache interface using Hazelcast.
*
*/
public class ClusteredCache implements Cache {
private static Logger logger = LoggerFactory.getLogger(ClusteredCache.class);
/**
* The map is used for distributed operations such as get, put, etc.
*/
protected IMap map;
private String name;
/**
* Create a new cache using the supplied named cache as the actual cache implementation
*
* @param name a name for the cache, which should be unique per vm.
* @param cache the cache implementation
*/
protected ClusteredCache(String name, IMap cache) {
map = cache;
setName(name);
}
public void addEntryListener(EntryListener listener, boolean includeValue) {
map.addEntryListener(listener, includeValue);
}
public void removeEntryListener(EntryListener listener) {
map.removeEntryListener(listener);
}
// Cache Interface
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object put(Object key, Object object) {
if (object == null) { return null; }
return map.put(key, object);
}
public Object get(Object key) {
return map.get(key);
}
public Object remove(Object key) {
return map.remove(key);
}
public void clear() {
map.clear();
}
public int size() {
LocalMapStats stats = map.getLocalMapStats();
return (int) (stats.getOwnedEntryCount() + stats.getBackupEntryCount());
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set keySet() {
return map.keySet();
}
public void putAll(Map entries) {
map.putAll(entries);
}
public Collection values() {
return map.values();
}
public long getCacheHits() {
return map.getLocalMapStats().getHits();
}
public long getCacheMisses() {
LocalMapStats stats = map.getLocalMapStats();
long gets = stats.getOperationStats().getNumberOfGets();
long hits = stats.getHits();
return gets > hits ? gets - hits : 0;
}
public int getCacheSize() {
LocalMapStats stats = map.getLocalMapStats();
return (int) (stats.getOwnedEntryMemoryCost() + stats.getBackupEntryMemoryCost());
}
public long getMaxCacheSize() {
return CacheFactory.getMaxCacheSize(getName());
}
public void setMaxCacheSize(int maxSize) {
CacheFactory.setMaxSizeProperty(getName(), maxSize);
}
public long getMaxLifetime() {
return CacheFactory.getMaxCacheLifetime(getName());
}
public void setMaxLifetime(long maxLifetime) {
CacheFactory.setMaxSizeProperty(getName(), maxLifetime);
}
public void destroy() {
map.destroy();
}
public boolean lock(Object key, long timeout) {
boolean result = true;
if (timeout < 0) {
map.lock(key);
} else if (timeout == 0) {
result = map.tryLock(key);
} else {
result = map.tryLock(key, timeout, TimeUnit.MILLISECONDS);
}
return result;
}
public boolean unlock(Object key) {
boolean result = true;
try { map.unlock(key); }
catch (IllegalMonitorStateException e) {
logger.error("Falied to release cluster lock", e);
result = false;
}
return result;
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cluster;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.Message;
/**
* Task that will broadcast a message to local connected client sessions.
*
* @author Gaston Dombiak
*/
public class BroadcastMessage implements ClusterTask {
private Message packet;
public BroadcastMessage() {
}
public BroadcastMessage(Message packet) {
this.packet = packet;
}
public Object getResult() {
// Not used since we are using #execute and not #query when using InvocationService
return null;
}
public void run() {
// Broadcast message to client sessions connected to this node
XMPPServer.getInstance().getRoutingTable().broadcastPacket(packet, true);
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSerializable(out, (DefaultElement) packet.getElement());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
Element packetElement = (Element) ExternalizableUtil.getInstance().readSerializable(in);
packet = new Message(packetElement, true);
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cluster;
import org.jivesoftware.openfire.RemotePacketRouter;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
/**
* Route packets to other nodes of the cluster. If the remote node was not found or failed
* to be reached then depending on the type of packet an error packet will be returned. In case
* the remote node is reached but the remote node fails to route the packet to the recipient (e.g.
* the recipient just left) then an error packet may be created from the remote node and send it
* back to this node.<p>
*
* @author Gaston Dombiak
*/
public class ClusterPacketRouter implements RemotePacketRouter {
private static Logger logger = LoggerFactory.getLogger(ClusterPacketRouter.class);
public boolean routePacket(byte[] nodeID, JID receipient, Packet packet) {
// Send the packet to the specified node and let the remote node deliver the packet to the recipient
try {
CacheFactory.doClusterTask(new RemotePacketExecution(receipient, packet), nodeID);
return true;
} catch (IllegalStateException e) {
logger.warn("Error while routing packet to remote node", e);
return false;
}
}
public void broadcastPacket(Message packet) {
// Execute the broadcast task across the cluster
CacheFactory.doClusterTask(new BroadcastMessage(packet));
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cluster;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.cluster.ClusterNodeInfo;
import org.jivesoftware.openfire.cluster.NodeID;
import com.hazelcast.core.Member;
/**
* Cluster Node information as provided by Hazelcast.
*
* @author Tom Evans
* @author Gaston Dombiak
*/
public class HazelcastClusterNodeInfo implements ClusterNodeInfo {
private String hostname;
private NodeID nodeID;
private long joinedTime;
private boolean seniorMember;
public HazelcastClusterNodeInfo(Member member) {
this(member, System.currentTimeMillis());
}
public HazelcastClusterNodeInfo(Member member, Long joinedTime) {
hostname = member.getInetSocketAddress().getHostName();
nodeID = NodeID.getInstance(member.getUuid().getBytes());
this.joinedTime = joinedTime;
seniorMember = ClusterManager.getSeniorClusterMember().equals(member.getUuid().getBytes());
}
public String getHostName() {
return hostname;
}
public NodeID getNodeID() {
return nodeID;
}
public long getJoinedTime() {
return joinedTime;
}
public boolean isSeniorMember() {
return seniorMember;
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cluster;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.util.cache.CacheFactory;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
/**
* A utility class which helps to gather Hazelcast stats and information.
*/
public class NodeRuntimeStats {
private static final ResourceBundle config = ResourceBundle.getBundle("hazelcast-runtime");
public static String getProviderConfig(String key) {
return config.getString(key);
}
/**
* Returns a Map of HazelcastRuntimeStats.NodeInfo objects keyed by cluster Member objects.
* A NodeInfo object is a collection of various Node stats.
*
* @return a Map of NodeInfo objects.
*/
public static Map<NodeID, NodeInfo> getNodeInfo() {
// Run cluster-wide stats query
Collection<Object> taskResult = CacheFactory.doSynchronousClusterTask(new NodeInfoTask(), true);
Map<NodeID, NodeInfo> result = new HashMap<NodeID, NodeInfo>();
for (Object tr : taskResult) {
NodeInfo nodeInfo = (NodeInfo) tr;
NodeID nodeId = NodeID.getInstance(nodeInfo.getNodeId());
result.put(nodeId, nodeInfo);
}
return result;
}
/**
* Clears the cache stats.
*/
public static void clearCacheStats() {
// not supported
}
/**
* Encapsulates statistics and information about a cluster node.
*/
public static class NodeInfoTask implements ClusterTask {
private Object result = null;
@Override
public void run() {
// Get runtime stats - mem and time:
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory();
long total = runtime.totalMemory();
long max = runtime.maxMemory();
long time = System.currentTimeMillis();
result = new NodeInfo(CacheFactory.getClusterMemberID(), free, total, max, time);
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSerializable(out, (NodeInfo) result);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
result = ExternalizableUtil.getInstance().readSerializable(in);
}
@Override
public Object getResult() { return result; }
}
/**
* Encapsulates statistics and information about a cluster node.
*/
public static class NodeInfo implements Serializable {
private byte[] nodeId;
private long freeMem;
private long totalMem;
private long maxMem;
private long time;
NodeInfo(byte[] nodeId, long freeMem, long totalMem, long maxMem, long time)
{
this.nodeId = nodeId;
this.freeMem = freeMem;
this.totalMem = totalMem;
this.maxMem = maxMem;
this.time = time;
}
/**
* Returns the amount of free memory in the cluster node's VM (in bytes).
*
* @return the amount of free memory on the cluster node.
*/
public byte[] getNodeId() {
return nodeId;
}
/**
* Returns the amount of free memory in the cluster node's VM (in bytes).
*
* @return the amount of free memory on the cluster node.
*/
public long getFreeMem() {
return freeMem;
}
/**
* Returns the total amount of memory in the cluster node's VM (in bytes).
*
* @return the total amount of memory on the cluster node.
*/
public long getTotalMem() {
return totalMem;
}
/**
* Returns the max amount of memory in the cluster node's VM (in bytes).
*
* @return the max amount of memory on the cluster node.
*/
public long getMaxMem() {
return maxMem;
}
/**
* Returns the current time on the cluster node in long format. This is useful
* monitoring information for applications that require the local times of each
* cluster member be to roughly in-synch (on top of the standard Coherence
* cluster time).<p>
*
* This value will always be somewhat inaccurate due to network delays, etc, so
* should only be taken as an approximate value.
*
* @return the local time of the cluster node.
*/
public long getTime() {
return time;
}
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 1999-2009 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jivesoftware.util.cluster;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
/**
* Task to be executed by remote nodes to deliver the requested packet to the requested
* receiver.
*
* @author Gaston Dombiak
*/
public class RemotePacketExecution implements ClusterTask {
private JID receipient;
private Packet packet;
public RemotePacketExecution() {
}
public RemotePacketExecution(JID receipient, Packet packet) {
this.receipient = receipient;
this.packet = packet;
}
public Object getResult() {
return null;
}
public void run() {
// Route packet to entity hosted by this node. If delivery fails then the routing table
// will inform the proper router of the failure and the router will handle the error reply logic
XMPPServer.getInstance().getRoutingTable().routePacket(receipient, packet, false);
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSafeUTF(out, receipient.toString());
if (packet instanceof IQ) {
ExternalizableUtil.getInstance().writeInt(out, 1);
}
else if (packet instanceof Message) {
ExternalizableUtil.getInstance().writeInt(out, 2);
}
else if (packet instanceof Presence) {
ExternalizableUtil.getInstance().writeInt(out, 3);
}
ExternalizableUtil.getInstance().writeSerializable(out, (DefaultElement) packet.getElement());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
String jid = ExternalizableUtil.getInstance().readSafeUTF(in);
// Optimization that was avoiding the need to reparse and validate JIDs but cannot be done due
// to a change in the JID API. We need to bring this constructor back
//receipient = new JID(jid, true);
receipient = new JID(jid);
int packetType = ExternalizableUtil.getInstance().readInt(in);
Element packetElement = (Element) ExternalizableUtil.getInstance().readSerializable(in);
switch (packetType) {
case 1:
packet = new IQ(packetElement, true);
break;
case 2:
packet = new Message(packetElement, true);
break;
case 3:
packet = new Presence(packetElement, true);
break;
}
}
public String toString() {
return super.toString() + " receipient: " + receipient + "packet: " + packet;
}
}
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment