Commit 3ea20bb3 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches/plugins@11403 b35dd754-fafc-0310-a699-88a17e54d16e
parent bc4dfc4f
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>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>
Clustering Plugin Changelog
</h1>
<p><b>1.2.0</b> -- Nov 10, 2009</p>
<ul>
<li>Changed license to Apache 2.0. Oracle Coherence should now be obtained separately.</li>
<li>ClustserListener is now compatible with distributed caches.</li>
</ul>
<p><b>1.1.1</b> -- June 4, 2008</p>
<ul>
<li>Fixed license validation problem.</li>
</ul>
<p><b>1.1.0</b> -- May 29, 2008</p>
<ul>
<li>Compatible version with Openfire 3.5.2. </li>
</ul>
<p><b>1.0.0</b> -- May 6, 2008</p>
<ul>
<li>Initial release. </li>
</ul>
</body>
</html>
<?xml version="1.0"?>
<!--
For detailed information on each of the elements that can be used in this
descriptor please see the Coherence Cache Configuration deployment descriptor
guide included in the Coherence distribution or on the web at:
http://www.tangosol.com/UserGuide-Reference-CacheConfig.jsp
-->
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
<caching-schemes>
<!-- scheme for partitioned cache -->
<distributed-scheme>
<scheme-name>distributed</scheme-name>
<service-name>DistributedCache</service-name>
<backing-map-scheme>
<local-scheme>
<scheme-ref>default-backing-map</scheme-ref>
</local-scheme>
</backing-map-scheme>
<!--<autostart>true</autostart>-->
</distributed-scheme>
<!-- Default Replicated caching scheme -->
<replicated-scheme>
<scheme-name>replicated</scheme-name>
<service-name>ReplicatedCache</service-name>
<backing-map-scheme>
<local-scheme>
<scheme-ref>default-backing-map</scheme-ref>
</local-scheme>
</backing-map-scheme>
</replicated-scheme>
<!-- scheme for partitioned cache fronted by near cache -->
<near-scheme>
<scheme-name>near-distributed</scheme-name>
<front-scheme>
<local-scheme>
<eviction-policy>LRU</eviction-policy>
<high-units>{near-size-high 1000}</high-units>
<low-units>{near-size-low 900}</low-units>
<expiry-delay>{near-expiry 5s}</expiry-delay>
<flush-delay>{near-flush 0}</flush-delay>
</local-scheme>
</front-scheme>
<back-scheme>
<distributed-scheme>
<scheme-ref>{distributed-scheme-name distributed}</scheme-ref>
</distributed-scheme>
</back-scheme>
<invalidation-strategy>{near-invalidation-strategy none}</invalidation-strategy>
<!--<autostart>true</autostart>-->
</near-scheme>
<!-- Optimistic caching scheme. -->
<optimistic-scheme>
<scheme-name>optimistic</scheme-name>
<service-name>OptimisticCache</service-name>
<backing-map-scheme>
<local-scheme>
<scheme-ref>default-backing-map</scheme-ref>
</local-scheme>
</backing-map-scheme>
<!--<autostart>true</autostart>-->
</optimistic-scheme>
<local-scheme>
<scheme-name>default-backing-map</scheme-name>
<class-name>{backing-map-class com.jivesoftware.util.cache.CoherenceCache}</class-name>
<eviction-policy>LRU</eviction-policy>
<high-units>{back-size-high 1000}</high-units>
<low-units>{back-size-low 900}</low-units>
<expiry-delay>{back-expiry 0}</expiry-delay>
<flush-delay>{back-flush 1m}</flush-delay>
</local-scheme>
<!-- scheme for service allowing cluster wide tasks -->
<invocation-scheme>
<scheme-name>OpenFire Cluster Service</scheme-name>
<service-name>OpenFire Cluster Service</service-name>
<autostart>true</autostart>
</invocation-scheme>
</caching-schemes>
<caching-scheme-mapping>
<!-- local caches -->
<cache-mapping>
<cache-name>POP3 Authentication</cache-name>
<scheme-name>default-backing-map</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>1h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>LDAP Authentication</cache-name>
<scheme-name>default-backing-map</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>2h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>File Transfer</cache-name>
<scheme-name>default-backing-map</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>10m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>File Transfer Cache</cache-name>
<scheme-name>default-backing-map</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>10m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<!-- optimistic caches -->
<!-- replicated caches -->
<cache-mapping>
<cache-name>opt-$cacheStats</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Clearspace SSO Nonce</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>2m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Client Session Info Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Javascript Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>262144</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>10d</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>235930</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Components Sessions</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Connection Managers Sessions</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Secret Keys Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Validated Domains</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Disco Server Features</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Disco Server Items</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Incoming Server Sessions</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Sessions by Hostname</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Entity Capabilities</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>48h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Routing Servers Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Routing Components Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Routing Users Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Routing AnonymousUsers Cache</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Directed Presences</cache-name>
<scheme-name>replicated</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<!-- partitioned caches -->
<cache-mapping>
<cache-name>Group Metadata Cache</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>5242880</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>15m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Routing User Sessions</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>VCard</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>10485760</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
<init-param>
<param-name>near-invalidation-strategy</param-name>
<param-value>present</param-value>
</init-param>
<init-param>
<param-name>near-expiry</param-name>
<param-value>30m</param-value>
</init-param>
<init-param>
<param-name>near-size-high</param-name>
<param-value>10000</param-value>
</init-param>
<init-param>
<param-name>near-size-low</param-name>
<param-value>9000</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Favicon Hits</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>262144</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>235930</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Favicon Misses</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>262144</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>235930</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Group</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>1048576</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>15m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Locked Out Accounts</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>1048576</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>15m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Last Activity Cache</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Multicast Service</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>24h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Offline Message Size</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>262144</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>12h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>235930</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Offline Presence Cache</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>1048576</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Privacy Lists</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>1048576</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Roster</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>10485760</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>6h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>User</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>10485760</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>30m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>943718</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Remote Users Existence</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>10m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Remote Server Configurations</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>524288</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>30m</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>471859</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Entity Capabilities Users</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>48h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
<cache-mapping>
<cache-name>Entity Capabilities Pending Hashes</cache-name>
<scheme-name>near-distributed</scheme-name>
<init-params>
<init-param>
<param-name>back-size-high</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>back-expiry</param-name>
<param-value>48h</param-value>
</init-param>
<init-param>
<param-name>back-size-low</param-name>
<param-value>0</param-value>
</init-param>
</init-params>
</cache-mapping>
</caching-scheme-mapping>
</cache-config>
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>com.jivesoftware.openfire.ClusteringPlugin</class>
<name>${plugin.name}</name>
<description>${plugin.description}</description>
<author>Jive Software</author>
<version>1.2.0</version>
<date>11/10/2009</date>
<minServerVersion>3.5.2</minServerVersion>
</plugin>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>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>
Clustering Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The clustering plugin adds support for running multiple redundant Openfire
servers together in a cluster. By running Openfire in a cluster, you can
distribute the load amongst a number of servers, as well as having some
form of redundency in the event that one of your servers dies. <font color="red">This
plugin requires a valid <a href="http://www.oracle.com/technology/products/coherence/index.html">Oracle Coherence</a> license.</font>
</p>
<h2>Installation</h2>
<p>
Follow steps 1 through 4 for adding Oracle Coherence libraries to Openfire. Step 5
explains how to add this plugin to your Openfire setup.
<ol>
<li>Get <a href="http://www.oracle.com/technology/products/coherence/index.html">Oracle Coherence for Java Version</a>.</li>
<li>Unzip the coherence file and locate <b>coherence.jar</b>, <b>coherence-work.jar</b> and <b>tangosol.jar</b> in folder coherence/lib.</li>
<li>Copy <b>coherence.jar</b>, <b>coherence-work.jar</b> and <b>tangosol.jar</b> to [openfire_home]/lib.</li>
<li>Restart Openfire server.</li>
<li>Copy clustering.jar into the plugins directory of your Openfire installation. The plugin will then be automatically deployed.</li>
</ol>
This plugin has been tested with Oracle Coherence Version 3.3.1/389. To upgrade
to a new Oracle Coherence version follow steps 1 through 4 as explained above.
To upgrade to a new version of the plugin just get the latest version from igniterealtime.org
and follow step 5.
</p>
<h2>Upgrading from Openfire Enterprise</h2>
<p>
To upgrade from Enterprise, you will need to shut down your server,
remove the <b>enterprise.jar</b> file and <b>enterprise</b> directory from
the plugins directory in your Openfire install root, and then follow the steps
outlined in the <i>Installation</i> section above.
</p>
<h2>Configuration</h2>
<p>
To enable clustering or monitor the cluster go to: Server --&gt; Server Manager --&gt; Clustering
</p>
<h2>Compiling from source code</h2>
<p>
If you want to modify or maintain this plugin you will need to get its source code and compile it.
Source code of this plugin is now available in the list of plugins that you get when you download
the <a href="http://www.igniterealtime.org/downloads/source.jsp">Openfire's source code</a>. Follow
these steps to have a working environment:
<ol>
<li>Get <a href="http://www.igniterealtime.org/downloads/source.jsp">Openfire's source code</a>.</li>
<li>Get <a href="http://www.oracle.com/technology/products/coherence/index.html">Oracle Coherence for Java Version</a>.</li>
<li>Unzip the coherence file and locate <b>coherence.jar</b>, <b>coherence-work.jar</b> and <b>tangosol.jar</b> in folder coherence/lib.</li>
<li>Copy <b>coherence.jar</b>, <b>coherence-work.jar</b> and <b>tangosol.jar</b> to [openfire]/build/lib.</li>
<li>Add <b>coherence.jar</b>, <b>coherence-work.jar</b> and <b>tangosol.jar</b> to your build path.</li>
</ol>
</p>
</body>
</html>
# $RCSfile$
# $Revision: 3148 $
# $Date: 2005-12-01 14:50:45 -0300 (Thu, 01 Dec 2005) $
##
## Clustering Resource Bundle
##
## Additional locales can be specified by creating a new resource file in this
## directory using the following conventions:
##
## clustering_i18n "_" language "_" country ".properties"
## clustering_i18n "_" language ".properties"
##
## e.g.
## clustering_i18n_en.propertis <- English resources
## clustering_i18n_en_US.properties <- American US resources
## clustering_i18n_de.properties <- German resources
## clustering_i18n_ja.properties <- Japanese resources
##
## Please note that the two digit language code should be lower case, and the
## two digit country code should be in uppercase. Often, it is not necessary to
## specify the country code.
##
## A full list of language codes can be found at
## http://www-old.ics.uci.edu/pub/ietf/http/related/iso639.txt
## and a full list of country codes can be found at
## http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
##
## In property strings that are parameterized, single quotes can be used to
## quote the "{" (curly brace) if necessary. A real single quote is represented by ''.
##
## REVISION HISTORY (by Enterprise version):
##
## 1.0.0
## Initial Release
plugin.name=Clustering Plugin
plugin.description=Clustering support for Openfire.
# $RCSfile$
# $Revision: 3148 $
# $Date: 2005-12-01 14:50:45 -0300 (Thu, 01 Dec 2005) $
##
## Clustering Resource Bundle - Czech locale (cs_CZ)
##
## For a full changelog, refer to the English bundle, clustering_i18n.properties.
##
plugin.name=Clustering Plugin
plugin.description=Clustering support for Openfire.
# $RCSfile$
# $Revision: $
# $Date: $
##
## Clustering Resource Bundle - Spanish locale (es)
##
## For a full changelog, refer to the English bundle, clustering_i18n.properties.
##
plugin.name=Clustering Plugin
plugin.description=Clustering support for Openfire.
# Java Resource Bundle
# Modified by Zaval JRC Editor (C) Zaval CE Group
# http://www.zaval.org/products/jrc-editor/
#
plugin.name=Clustering Plugin
plugin.description=Clustering support for Openfire.
# $RCSfile$
# $Revision: 3148 $
# $Date: 2005-12-01 14:50:45 -0300 (Thu, 01 Dec 2005) $
##
## Clustering Resource Bundle - Chinese locale (zh_CN)
##
## For a full changelog, refer to the English bundle, clustering_i18n.properties
.
##
plugin.name=Clustering Plugin
plugin.description=Clustering support for Openfire.
/**
* $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 com.jivesoftware.openfire.session.RemoteSessionLocator;
import com.jivesoftware.util.cache.CoherenceExternalizableUtil;
import com.jivesoftware.util.cluster.CoherencePacketRouter;
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.Log;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.jivesoftware.util.cache.ExternalizableUtilStrategy;
import java.io.File;
import java.io.FileFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
/**
* Clustering Enterprise plugin.
*
* @author Matt Tucker
*/
public class ClusteringPlugin implements Plugin, PropertyEventListener {
private static final String COHERENCE_CONFIG = "tangosol-coherence-override";
private static final String COHERENCE_CACHE_CONFIG = "coherence-cache-config";
/**
* 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 we Enterprise 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"));
}
});
if (jars.length > 0) {
// Do not load this plugin since Enterprise is still installed
System.out.println("Enterprise plugin found. Stopping Clustering Plugin");
throw new IllegalStateException("This plugin cannot run next to the Enterprise plugin");
}
// Make sure that the enteprise folder exists under the home directory
File enterpriseDir = new File(JiveGlobals.getHomeDirectory() +
File.separator + "enterprise");
if (!enterpriseDir.exists()) {
enterpriseDir.mkdirs();
}
// Check if Coherence libs are installed and stop loading this plugin if NOT found
// File libDir = new File(JiveGlobals.getHomeDirectory(), "lib");
// jars = libDir.listFiles(new FileFilter() {
// public boolean accept(File pathname) {
// String fileName = pathname.getName().toLowerCase();
// return (fileName.equalsIgnoreCase("coherence.jar"));
// }
// });
// if (jars.length == 0) {
// // Do not load this plugin since Coherence libs are not installed
// System.out.println("Coherence libs not found. Stopping Clustering Plugin. Copy tangosol.jar, " +
// "coherence.jar and coherence-work.jar files to [OPENFIRE_HOME]/lib and restart the server.");
// throw new IllegalStateException("Coherence libs not found. Stopping Clustering Plugin. Copy " +
// "tangosol.jar, coherence.jar and coherence-work.jar files to [OPENFIRE_HOME]/lib and restart the server.");
// }
// List for clustering setting events (e.g. enabled/disabled)
PropertyEventDispatcher.addListener(this);
// Delete no longer used COHERENCE_CONFIG file. Java system properties should be used
// to customize coherence
File configFile = new File(enterpriseDir, COHERENCE_CONFIG + ".xml");
if (configFile.exists()) {
configFile.delete();
}
// Delete no longer used COHERENCE_CACHE_CONFIG file. Admins should use system properties
// to override default values. Same system properties will be used when not using enterprise or not
// using clustering
configFile = new File(enterpriseDir, COHERENCE_CACHE_CONFIG + ".xml");
if (configFile.exists()) {
configFile.delete();
}
try {
// Add openfireHome/enterprise dir to pluginclassloader
// Add enterprise plugin dir to pluginclassloader
URL url = new File(pluginDirectory + File.separator).toURL();
manager.getPluginClassloader(manager.getPlugin(pluginDirectory.getName())).addURLFile(url);
}
catch (MalformedURLException e) {
Log.error("Error adding openfireHome/enterprise to the classpath of the enterprise plugin", e);
}
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 CoherenceExternalizableUtil());
// 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 CoherencePacketRouter());
}
/**
* Returns the date when this release of Openfire Enterprise was released.
*
* @return the date when this release of Openfire Enterprise was released.
*/
public static Date getReleaseDate() {
try {
// @DATE@ should be replaced with a date with the following format: Jan 31, 2007
// Automaticly set by ANT build tasks
return DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US).parse("@DATE@");
}
catch (ParseException e) {
Log.error("Error parsing date", e);
return null;
}
}
public void destroyPlugin() {
ClusterManager.shutdown();
// Set the old serialization strategy was using before enterprise 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.SessionManager;
import org.jivesoftware.openfire.privacy.PrivacyList;
import org.jivesoftware.openfire.privacy.PrivacyListManager;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.ClientSessionInfo;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.ClusterTask;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Surrogate for client sessions hosted in some remote cluster node.
*
* @author Gaston Dombiak
*/
public class RemoteClientSession extends RemoteSession implements ClientSession {
private long initialized = -1;
public RemoteClientSession(byte[] nodeID, JID address) {
super(nodeID, address);
}
public PrivacyList getActiveList() {
Cache<String, ClientSessionInfo> cache = SessionManager.getInstance().getSessionInfoCache();
ClientSessionInfo sessionInfo = cache.get(getAddress().toString());
if (sessionInfo != null && sessionInfo.getActiveList() != null) {
return PrivacyListManager.getInstance().getPrivacyList(address.getNode(), sessionInfo.getActiveList());
}
return null;
}
public void setActiveList(PrivacyList activeList) {
// Highly unlikely that a list is change to a remote session but still possible
doClusterTask(new SetPrivacyListTask(address, true, activeList));
}
public PrivacyList getDefaultList() {
Cache<String, ClientSessionInfo> cache = SessionManager.getInstance().getSessionInfoCache();
ClientSessionInfo sessionInfo = cache.get(getAddress().toString());
if (sessionInfo != null && sessionInfo.getDefaultList() != null) {
return PrivacyListManager.getInstance().getPrivacyList(address.getNode(), sessionInfo.getDefaultList());
}
return null;
}
public void setDefaultList(PrivacyList defaultList) {
// Highly unlikely that a list is change to a remote session but still possible
doClusterTask(new SetPrivacyListTask(address, false, defaultList));
}
public String getUsername() throws UserNotFoundException {
return address.getNode();
}
public boolean isAnonymousUser() {
return SessionManager.getInstance().isAnonymousRoute(getAddress());
}
public boolean isInitialized() {
if (initialized == -1) {
Presence presence = getPresence();
if (presence != null && presence.isAvailable()) {
// Optimization to avoid making a remote call
initialized = 1;
}
else {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.isInitialized);
initialized = (Boolean) doSynchronousClusterTask(task) ? 1 : 0;
}
}
return initialized == 1;
}
public void setInitialized(boolean isInit) {
doClusterTask(new SetInitializedTask(address, isInit));
}
public boolean canFloodOfflineMessages() {
// Code copied from LocalClientSession to avoid remote calls
if(isOfflineFloodStopped()) {
return false;
}
String username = getAddress().getNode();
for (ClientSession session : SessionManager.getInstance().getSessions(username)) {
if (session.isOfflineFloodStopped()) {
return false;
}
}
return true;
}
public boolean isOfflineFloodStopped() {
Cache<String, ClientSessionInfo> cache = SessionManager.getInstance().getSessionInfoCache();
ClientSessionInfo sessionInfo = cache.get(getAddress().toString());
return sessionInfo != null && sessionInfo.isOfflineFloodStopped();
}
public Presence getPresence() {
Cache<String,ClientSessionInfo> cache = SessionManager.getInstance().getSessionInfoCache();
ClientSessionInfo sessionInfo = cache.get(getAddress().toString());
if (sessionInfo != null) {
return sessionInfo.getPresence();
}
return null;
}
public void setPresence(Presence presence) {
try {
doClusterTask(new SetPresenceTask(address, presence));
} catch (IllegalStateException e) {
// Remote node is down
if (presence.getType() == Presence.Type.unavailable) {
// Ignore unavailable presence (since session is already unavailable - at least to us)
return;
}
throw e;
}
}
public int incrementConflictCount() {
ClusterTask task = getRemoteSessionTask(RemoteSessionTask.Operation.incrementConflictCount);
return (Integer) doSynchronousClusterTask(task);
}
RemoteSessionTask getRemoteSessionTask(RemoteSessionTask.Operation operation) {
return new ClientSessionTask(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 SetPresenceTask extends ClientSessionTask {
private Presence presence;
public SetPresenceTask() {
super();
}
protected SetPresenceTask(JID address, Presence presence) {
super(address, null);
this.presence = presence;
}
public void run() {
((ClientSession)getSession()).setPresence(presence);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSerializable(out, (DefaultElement) presence.getElement());
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
Element packetElement = (Element) ExternalizableUtil.getInstance().readSerializable(in);
presence = new Presence(packetElement, true);
}
}
private static class SetPrivacyListTask extends ClientSessionTask {
private boolean activeList;
private String listName;
public SetPrivacyListTask() {
super();
}
protected SetPrivacyListTask(JID address, boolean activeList, PrivacyList list) {
super(address, null);
this.activeList = activeList;
this.listName = list != null ? list.getName() : null;
}
public void run() {
ClientSession session = ((ClientSession) getSession());
PrivacyList list = null;
// Get the privacy list to set
if (listName != null) {
try {
String username = session.getUsername();
list = PrivacyListManager.getInstance().getPrivacyList(username, listName);
} catch (UserNotFoundException e) {
// Should never happen
}
}
// Set the privacy list to the session
if (activeList) {
session.setActiveList(list);
}
else {
session.setDefaultList(list);
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeBoolean(out, activeList);
ExternalizableUtil.getInstance().writeBoolean(out, listName != null);
if (listName != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, listName);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
activeList = ExternalizableUtil.getInstance().readBoolean(in);
if (ExternalizableUtil.getInstance().readBoolean(in)) {
listName = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
}
private static class SetInitializedTask extends ClientSessionTask {
private boolean initialized;
public SetInitializedTask() {
super();
}
protected SetInitializedTask(JID address, boolean initialized) {
super(address, null);
this.initialized = initialized;
}
public void run() {
((ClientSession) getSession()).setInitialized(initialized);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeBoolean(out, initialized);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
initialized = ExternalizableUtil.getInstance().readBoolean(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.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 com.tangosol.util.MapEvent;
import com.tangosol.util.MapListener;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.NodeID;
import java.util.Set;
/**
* 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 Pete Matern
* @author Gaston Dombiak
*/
abstract class CacheListener implements MapListener {
protected final String cacheName;
private ClusterListener clusterListener;
public CacheListener(ClusterListener clusterListener, String cacheName) {
this.clusterListener = clusterListener;
this.cacheName = cacheName;
}
public void entryInserted(MapEvent mapEvent) {
handleMapEvent(mapEvent, false);
}
public void entryUpdated(MapEvent mapEvent) {
handleMapEvent(mapEvent, false);
}
public void entryDeleted(MapEvent mapEvent) {
handleMapEvent(mapEvent, true);
}
void handleMapEvent(MapEvent mapEvent, boolean removal) {
NodeID nodeID = getNodeID(mapEvent, removal);
//ignore items which this node has added
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
Set<String> sessionJIDS = clusterListener.lookupJIDList(nodeID, cacheName);
if (removal) {
sessionJIDS.remove(mapEvent.getKey().toString());
}
else {
sessionJIDS.add(mapEvent.getKey().toString());
}
}
}
abstract NodeID getNodeID(MapEvent mapEvent, boolean removal);
}
/**
* $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 org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginClassLoader;
import org.jivesoftware.openfire.container.PluginManager;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
/**
* Class loader to be used by coherence to load classes that live in the enterprise plugin,
* the Openfire core and also classes defined in other plugins. With this new class loader
* plugins can now make use of coherence.<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. Coherence 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 enterprise plugin that
* includes checking the Openfire core. If not found then try with the other plugins.
*
* @author Gaston Dombiak
*/
public class ClusterClassLoader extends ClassLoader {
private PluginClassLoader enterpriseClassloader;
public ClusterClassLoader() {
super();
Plugin plugin = XMPPServer.getInstance().getPluginManager().getPlugin("clustering");
enterpriseClassloader = XMPPServer.getInstance().getPluginManager().getPluginClassloader(plugin);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
return enterpriseClassloader.loadClass(name);
}
catch (ClassNotFoundException e) {
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
for (Plugin plugin : pluginManager.getPlugins()) {
String pluginName = pluginManager.getPluginDirectory(plugin).getName();
if ("clustering".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 = enterpriseClassloader.getResource(name);
if (resource == null) {
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
for (Plugin plugin : pluginManager.getPlugins()) {
String pluginName = pluginManager.getPluginDirectory(plugin).getName();
if ("clustering".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 = enterpriseClassloader.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 ("clustering".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 com.tangosol.net.MemberEvent;
import com.tangosol.net.MemberListener;
import com.tangosol.util.MapEvent;
import com.tangosol.util.MapListener;
import com.tangosol.util.UID;
import com.tangosol.util.filter.MapEventFilter;
import org.jivesoftware.openfire.PacketException;
import org.jivesoftware.openfire.RoutingTable;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.openfire.handler.DirectedPresence;
import org.jivesoftware.openfire.handler.PresenceUpdateHandler;
import org.jivesoftware.openfire.session.IncomingServerSession;
import org.jivesoftware.openfire.session.RemoteSessionLocator;
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.CacheWrapper;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
/**
* ClusterListener reacts to membership changes in the cluster. It takes care of cleaning up the state
* of the routing table and the sessions within it when a node which manages those sessions goes down.
*/
public class ClusterListener implements MemberListener {
private static final int C2S_CACHE_IDX = 0;
private static final int ANONYMOUS_C2S_CACHE_IDX = 1;
private static final int S2S_CACHE_NAME_IDX= 2;
private static final int COMPONENT_CACHE_IDX= 3;
private static final int COMPONENT_SESSION_CACHE_IDX = 4;
private static final int CM_CACHE_IDX = 5;
private static final int ISS_CACHE_IDX = 6;
/**
* Caches stored in RoutingTable
*/
Cache C2SCache;
Cache anonymousC2SCache;
Cache S2SCache;
Cache componentsCache;
/**
* Caches stored in SessionManager
*/
Cache componentSessionsCache;
Cache multiplexerSessionsCache;
Cache incomingServerSessionsCache;
/**
* Caches stored in PresenceUpdateHandler
*/
Cache directedPresencesCache;
private Map<NodeID, Set<String>[]> nodeSessions = new ConcurrentHashMap<NodeID, Set<String>[]>();
private Map<NodeID, Map<String, Collection<String>>> nodePresences = new ConcurrentHashMap<NodeID, Map<String, Collection<String>>>();
private boolean seniorClusterMember = CacheFactory.isSeniorClusterMember();
private Map<Cache, MapListener> mapListeners = new HashMap<Cache, MapListener>();
/**
* Flag that indicates if the listener has done all clean up work when noticed that the
* cluster has been stopped. This will force the EnterprisePlugin to wait until all clean
* up (e.g. changing caches implementations) is done before destroying the plugin.
*/
private boolean done = false;
public ClusterListener() {
C2SCache = CacheFactory.createCache(RoutingTableImpl.C2S_CACHE_NAME);
anonymousC2SCache = CacheFactory.createCache(RoutingTableImpl.ANONYMOUS_C2S_CACHE_NAME);
S2SCache = CacheFactory.createCache(RoutingTableImpl.S2S_CACHE_NAME);
componentsCache = CacheFactory.createCache(RoutingTableImpl.COMPONENT_CACHE_NAME);
componentSessionsCache = CacheFactory.createCache(SessionManager.COMPONENT_SESSION_CACHE_NAME);
multiplexerSessionsCache = CacheFactory.createCache(SessionManager.CM_CACHE_NAME);
incomingServerSessionsCache = CacheFactory.createCache(SessionManager.ISS_CACHE_NAME);
directedPresencesCache = CacheFactory.createCache(PresenceUpdateHandler.PRESENCE_CACHE_NAME);
addMapListener(C2SCache, new ClientSessionListener(this, C2SCache.getName()));
addMapListener(anonymousC2SCache, new ClientSessionListener(this, anonymousC2SCache.getName()));
addMapListener(S2SCache, new DefaultCacheListener(this, S2SCache.getName()));
addMapListener(componentsCache, new ComponentCacheListener());
addMapListener(componentSessionsCache, new DefaultCacheListener(this, componentSessionsCache.getName()));
addMapListener(multiplexerSessionsCache, new DefaultCacheListener(this, multiplexerSessionsCache.getName()));
addMapListener(incomingServerSessionsCache, new DefaultCacheListener(this, incomingServerSessionsCache.getName()));
addMapListener(directedPresencesCache, new DirectedPresenceListener());
// Simulate insert events of existing content
simuateCacheInserts(C2SCache);
simuateCacheInserts(anonymousC2SCache);
simuateCacheInserts(S2SCache);
simuateCacheInserts(componentsCache);
simuateCacheInserts(componentSessionsCache);
simuateCacheInserts(multiplexerSessionsCache);
simuateCacheInserts(incomingServerSessionsCache);
simuateCacheInserts(directedPresencesCache);
}
private void addMapListener(Cache cache, MapListener listener) {
if (cache instanceof CacheWrapper) {
Cache wrapped = ((CacheWrapper)cache).getWrappedCache();
if (wrapped instanceof ClusteredCache) {
((ClusteredCache)wrapped).addMapListener(listener, new MapEventFilter(MapEventFilter.E_KEYSET), false);
// Keep track of the listener that we added to the cache
mapListeners.put(cache, listener);
}
}
}
private void simuateCacheInserts(Cache<Object, Object> cache) {
MapListener mapListener = mapListeners.get(cache);
if (mapListener != null) {
if (cache instanceof CacheWrapper) {
Cache wrapped = ((CacheWrapper) cache).getWrappedCache();
if (wrapped instanceof ClusteredCache) {
ClusteredCache clusteredCache = (ClusteredCache) wrapped;
for (Map.Entry entry : cache.entrySet()) {
MapEvent event = new MapEvent(clusteredCache.map, MapEvent.ENTRY_INSERTED, entry.getKey(), null,
entry.getValue());
mapListener.entryInserted(event);
}
}
}
}
}
Set<String> lookupJIDList(NodeID nodeKey, String cacheName) {
Set<String>[] allLists = nodeSessions.get(nodeKey);
if (allLists == null) {
allLists = insertJIDList(nodeKey);
}
if (cacheName.equals(C2SCache.getName())) {
return allLists[C2S_CACHE_IDX];
}
else if (cacheName.equals(anonymousC2SCache.getName())) {
return allLists[ANONYMOUS_C2S_CACHE_IDX];
}
else if (cacheName.equals(S2SCache.getName())) {
return allLists[S2S_CACHE_NAME_IDX];
}
else if (cacheName.equals(componentsCache.getName())) {
return allLists[COMPONENT_CACHE_IDX];
}
else if (cacheName.equals(componentSessionsCache.getName())) {
return allLists[COMPONENT_SESSION_CACHE_IDX];
}
else if (cacheName.equals(multiplexerSessionsCache.getName())) {
return allLists[CM_CACHE_IDX];
}
else if (cacheName.equals(incomingServerSessionsCache.getName())) {
return allLists[ISS_CACHE_IDX];
}
else {
throw new IllegalArgumentException("Unknown cache name: " + cacheName);
}
}
private Set<String>[] insertJIDList(NodeID nodeKey) {
Set<String>[] allLists = new Set[] {
new HashSet<String>(),
new HashSet<String>(),
new HashSet<String>(),
new HashSet<String>(),
new HashSet<String>(),
new HashSet<String>(),
new HashSet<String>()
};
nodeSessions.put(nodeKey, allLists);
return allLists;
}
public boolean isDone() {
return done;
}
public void memberJoined(MemberEvent memberEvent) {
if (memberEvent.isLocal()) {
done = false;
// We left and re-joined the cluster
Log.info("Rejoining cluster as node: " + new UID(CacheFactory.getClusterMemberID()) + ". Senior Member: " +
(CacheFactory.isSeniorClusterMember() ? "YES" : "NO"));
// Simulate insert events of existing cache content
simuateCacheInserts(C2SCache);
simuateCacheInserts(anonymousC2SCache);
simuateCacheInserts(S2SCache);
simuateCacheInserts(componentsCache);
simuateCacheInserts(componentSessionsCache);
simuateCacheInserts(multiplexerSessionsCache);
simuateCacheInserts(incomingServerSessionsCache);
simuateCacheInserts(directedPresencesCache);
// Set the new ID of this cluster node
XMPPServer.getInstance().setNodeID(NodeID.getInstance(CacheFactory.getClusterMemberID()));
// Trigger events
ClusterManager.fireJoinedCluster(true);
if (CacheFactory.isSeniorClusterMember()) {
seniorClusterMember = true;
ClusterManager.fireMarkedAsSeniorClusterMember();
}
}
else {
nodePresences.put(NodeID.getInstance(memberEvent.getMember().getUid().toByteArray()),
new ConcurrentHashMap<String, Collection<String>>());
// Trigger event that a new node has joined the cluster
ClusterManager.fireJoinedCluster(memberEvent.getMember().getUid().toByteArray(), true);
}
}
public void memberLeaving(MemberEvent memberEvent) {
// Ignore
}
public void memberLeft(MemberEvent memberEvent) {
byte[] nodeID = memberEvent.getMember().getUid().toByteArray();
if (memberEvent.isLocal()) {
Log.info("Leaving cluster");
// This node may have realized that it got kicked out of the cluster
seniorClusterMember = false;
// Clean up all traces. This will set all remote sessions as unavailable
List<NodeID> nodeIDs = new ArrayList<NodeID>(nodeSessions.keySet());
// Revert cluster caches to local caches
CacheFactory.leftCluster();
// Trigger event. Wait until the listeners have processed the event. Caches will be populated
// again with local content.
ClusterManager.fireLeftCluster();
if (!XMPPServer.getInstance().isShuttingDown()) {
for (NodeID key : nodeIDs) {
// Clean up directed presences sent from entites hosted in the leaving node to local entities
// Clean up directed presences sent to entites hosted in the leaving node from local entities
cleanupDirectedPresences(key);
// Clean up no longer valid sessions
cleanupPresences(key);
}
// Remove traces of directed presences sent from local entities to handlers that no longer exist
// At this point c2s sessions are gone from the routing table so we can identify expired sessions
XMPPServer.getInstance().getPresenceUpdateHandler().removedExpiredPresences();
}
// Mark that we are done with the clean up
done = true;
}
else {
// Trigger event that a node left the cluster
ClusterManager.fireLeftCluster(nodeID);
// Clean up directed presences sent from entites hosted in the leaving node to local entities
// Clean up directed presences sent to entites hosted in the leaving node from local entities
cleanupDirectedPresences(NodeID.getInstance(nodeID));
if (!seniorClusterMember && CacheFactory.isSeniorClusterMember()) {
seniorClusterMember = true;
ClusterManager.fireMarkedAsSeniorClusterMember();
}
if (CacheFactory.isSeniorClusterMember()) {
cleanupNode(NodeID.getInstance(nodeID));
}
// Remove traces of directed presences sent from local entities to handlers that no longer exist.
// At this point c2s sessions are gone from the routing table so we can identify expired sessions
XMPPServer.getInstance().getPresenceUpdateHandler().removedExpiredPresences();
}
// Delete nodeID instance (release from memory)
NodeID.deleteInstance(nodeID);
}
private void cleanupDirectedPresences(NodeID nodeID) {
// Remove traces of directed presences sent from node that is gone to entities hosted in this JVM
Map<String, Collection<String>> senders = nodePresences.remove(nodeID);
if (senders != null) {
for (Map.Entry<String, Collection<String>> entry : senders.entrySet()) {
String sender = entry.getKey();
Collection<String> receivers = entry.getValue();
for (String receiver : receivers) {
try {
Presence presence = new Presence(Presence.Type.unavailable);
presence.setFrom(sender);
presence.setTo(receiver);
XMPPServer.getInstance().getPresenceRouter().route(presence);
}
catch (PacketException e) {
Log.error(e);
}
}
}
}
}
/**
* Executes close logic for each session hosted in the remote node that is
* no longer available. This logic is similar to the close listeners used by
* the {@link SessionManager}.<p>
*
* If the node that went down performed its own clean up logic then the other
* cluster nodes will have the correct state. That means that this method
* will not find any sessions to remove.<p>
*
* If this operation is too big and we are still in a cluster then we can
* distribute the work in the cluster to go faster.
*
* @param key the key that identifies the node that is no longer available.
*/
private void cleanupNode(NodeID key) {
// TODO Fork in another process and even ask other nodes to process work
RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
RemoteSessionLocator sessionLocator = XMPPServer.getInstance().getRemoteSessionLocator();
SessionManager manager = XMPPServer.getInstance().getSessionManager();
// TODO Consider removing each cached entry once processed instead of all at the end. Could be more error-prove.
Set<String> registeredUsers = lookupJIDList(key, C2SCache.getName());
if (!registeredUsers.isEmpty()) {
for (String fullJID : new ArrayList<String>(registeredUsers)) {
JID offlineJID = new JID(fullJID);
manager.removeSession(null, offlineJID, false, true);
}
}
Set<String> anonymousUsers = lookupJIDList(key, anonymousC2SCache.getName());
if (!anonymousUsers.isEmpty()) {
for (String fullJID : new ArrayList<String>(anonymousUsers)) {
JID offlineJID = new JID(fullJID);
manager.removeSession(null, offlineJID, true, true);
}
}
// Remove outgoing server sessions hosted in node that left the cluster
Set<String> remoteServers = lookupJIDList(key, S2SCache.getName());
if (!remoteServers.isEmpty()) {
for (String fullJID : new ArrayList<String>(remoteServers)) {
JID serverJID = new JID(fullJID);
routingTable.removeServerRoute(serverJID);
}
}
Set<String> components = lookupJIDList(key, componentsCache.getName());
if (!components.isEmpty()) {
for (String address : new ArrayList<String>(components)) {
Lock lock = CacheFactory.getLock(address, componentsCache);
try {
lock.lock();
Set<NodeID> nodes = (Set<NodeID>) componentsCache.get(address);
if (nodes != null) {
nodes.remove(key);
if (nodes.isEmpty()) {
componentsCache.remove(address);
}
else {
componentsCache.put(address, nodes);
}
}
} finally {
lock.unlock();
}
}
}
Set<String> componentSessions = lookupJIDList(key, componentSessionsCache.getName());
if (!componentSessions.isEmpty()) {
for (String domain : new ArrayList<String>(componentSessions)) {
componentSessionsCache.remove(domain);
// Registered subdomains of external component will be removed
// by the clean up of the component cache
}
}
Set<String> multiplexers = lookupJIDList(key, multiplexerSessionsCache.getName());
if (!multiplexers.isEmpty()) {
for (String fullJID : new ArrayList<String>(multiplexers)) {
multiplexerSessionsCache.remove(fullJID);
// c2s connections connected to node that went down will be cleaned up
// by the c2s logic above. If the CM went down and the node is up then
// connections will be cleaned up as usual
}
}
Set<String> incomingSessions = lookupJIDList(key, incomingServerSessionsCache.getName());
if (!incomingSessions.isEmpty()) {
for (String streamID : new ArrayList<String>(incomingSessions)) {
IncomingServerSession session = sessionLocator.getIncomingServerSession(key.toByteArray(), streamID);
// Remove all the hostnames that were registered for this server session
for (String hostname : session.getValidatedDomains()) {
manager.unregisterIncomingServerSession(hostname, session);
}
}
}
nodeSessions.remove(key);
// TODO Make sure that routing table has no entry referring to node that is gone
}
/**
* Simulate an unavailable presence for sessions that were being hosted in other
* cluster nodes. This method should be used ONLY when this JVM left the cluster.
*
* @param key the key that identifies the node that is no longer available.
*/
private void cleanupPresences(NodeID key) {
Set<String> registeredUsers = lookupJIDList(key, C2SCache.getName());
if (!registeredUsers.isEmpty()) {
for (String fullJID : new ArrayList<String>(registeredUsers)) {
JID offlineJID = new JID(fullJID);
try {
Presence presence = new Presence(Presence.Type.unavailable);
presence.setFrom(offlineJID);
XMPPServer.getInstance().getPresenceRouter().route(presence);
}
catch (PacketException e) {
Log.error(e);
}
}
}
Set<String> anonymousUsers = lookupJIDList(key, anonymousC2SCache.getName());
if (!anonymousUsers.isEmpty()) {
for (String fullJID : new ArrayList<String>(anonymousUsers)) {
JID offlineJID = new JID(fullJID);
try {
Presence presence = new Presence(Presence.Type.unavailable);
presence.setFrom(offlineJID);
XMPPServer.getInstance().getPresenceRouter().route(presence);
}
catch (PacketException e) {
Log.error(e);
}
}
}
nodeSessions.remove(key);
}
/**
* MapListener implementation tracks events for caches whose value is a nodeID.
*/
private static class DefaultCacheListener extends CacheListener {
public DefaultCacheListener(ClusterListener clusterListener, String cacheName) {
super(clusterListener, cacheName);
}
NodeID getNodeID(MapEvent mapEvent, boolean removal) {
Object value = removal ? mapEvent.getOldValue() : mapEvent.getNewValue();
return NodeID.getInstance((byte[])value);
}
}
/**
* MapListener implementation tracks events for caches of c2s sessions.
*/
private static class ClientSessionListener extends CacheListener {
public ClientSessionListener(ClusterListener clusterListener, String cacheName) {
super(clusterListener, cacheName);
}
NodeID getNodeID(MapEvent mapEvent, boolean removal) {
Object value = removal ? mapEvent.getOldValue() : mapEvent.getNewValue();
return ((ClientRoute)value).getNodeID();
}
}
/**
* MapListener implementation tracks events for caches of c2s sessions.
*/
private class DirectedPresenceListener implements MapListener {
public void entryInserted(MapEvent mapEvent) {
byte[] nodeID = getNodeID(mapEvent, false);
// Ignore events origintated from this JVM
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
// Check if the directed presence was sent to an entity hosted by this JVM
RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
String sender = mapEvent.getKey().toString();
Collection<String> handlers = new HashSet<String>();
for (JID handler : getHandlers(mapEvent)) {
if (routingTable.isLocalRoute(handler)) {
// Keep track of the remote sender and local handler that got the directed presence
handlers.addAll(getReceivers(mapEvent, handler));
}
}
if (!handlers.isEmpty()) {
Map<String, Collection<String>> senders = nodePresences.get(NodeID.getInstance(nodeID));
if (senders == null) {
senders = new ConcurrentHashMap<String, Collection<String>>();
nodePresences.put(NodeID.getInstance(nodeID), senders);
}
senders.put(sender, handlers);
}
}
}
public void entryUpdated(MapEvent mapEvent) {
byte[] nodeID = getNodeID(mapEvent, false);
// Ignore events origintated from this JVM
if (nodeID != null && !XMPPServer.getInstance().getNodeID().equals(nodeID)) {
// Check if the directed presence was sent to an entity hosted by this JVM
RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
String sender = mapEvent.getKey().toString();
Collection<String> handlers = new HashSet<String>();
for (JID handler : getHandlers(mapEvent)) {
if (routingTable.isLocalRoute(handler)) {
// Keep track of the remote sender and local handler that got the directed presence
handlers.addAll(getReceivers(mapEvent, handler));
}
}
Map<String, Collection<String>> senders = nodePresences.get(NodeID.getInstance(nodeID));
if (senders == null) {
senders = new ConcurrentHashMap<String, Collection<String>>();
nodePresences.put(NodeID.getInstance(nodeID), senders);
}
if (!handlers.isEmpty()) {
senders.put(sender, handlers);
}
else {
// Remove any traces of the sender since no directed presence was sent to this JVM
senders.remove(sender);
}
}
}
public void entryDeleted(MapEvent mapEvent) {
if (mapEvent.getNewValue() == null && ((Collection)mapEvent.getOldValue()).isEmpty()) {
// Nothing to remove
return;
}
byte[] nodeID = getNodeID(mapEvent, true);
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
String sender = mapEvent.getKey().toString();
nodePresences.get(NodeID.getInstance(nodeID)).remove(sender);
}
}
byte[] getNodeID(MapEvent mapEvent, boolean removal) {
Object value = removal ? mapEvent.getOldValue() : mapEvent.getNewValue();
Collection<DirectedPresence> directedPresences = (Collection<DirectedPresence>) value;
if (directedPresences.isEmpty()) {
Log.warn("ClusteringListener - Found empty directed presences for sender: " + mapEvent.getKey());
return null;
}
return directedPresences.iterator().next().getNodeID();
}
Collection<JID> getHandlers(MapEvent mapEvent) {
Object value = mapEvent.getNewValue();
Collection<JID> answer = new ArrayList<JID>();
for (DirectedPresence directedPresence : (Collection<DirectedPresence>)value) {
answer.add(directedPresence.getHandler());
}
return answer;
}
Set<String> getReceivers(MapEvent mapEvent, JID handler) {
Object value = mapEvent.getNewValue();
for (DirectedPresence directedPresence : (Collection<DirectedPresence>)value) {
if (directedPresence.getHandler().equals(handler)) {
return directedPresence.getReceivers();
}
}
return Collections.emptySet();
}
}
/**
* MapListener implementation tracks events for caches of internal/external components.
*/
private class ComponentCacheListener implements MapListener {
public void entryInserted(MapEvent mapEvent) {
Object newValue = mapEvent.getNewValue();
if (newValue != null) {
for (NodeID nodeID : (Set<NodeID>) newValue) {
//ignore items which this node has added
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
Set<String> sessionJIDS = lookupJIDList(nodeID, componentsCache.getName());
sessionJIDS.add(mapEvent.getKey().toString());
}
}
}
}
public void entryUpdated(MapEvent mapEvent) {
// Remove any trace to the component that was added/deleted to some node
String domain = mapEvent.getKey().toString();
for (Map.Entry<NodeID, Set<String>[]> entry : nodeSessions.entrySet()) {
// Get components hosted in this node
Set<String> nodeComponents = entry.getValue()[COMPONENT_CACHE_IDX];
nodeComponents.remove(domain);
}
// Trace nodes hosting the component
entryInserted(mapEvent);
}
public void entryDeleted(MapEvent mapEvent) {
Object newValue = mapEvent.getNewValue();
if (newValue != null) {
for (NodeID nodeID : (Set<NodeID>) newValue) {
//ignore items which this node has added
if (!XMPPServer.getInstance().getNodeID().equals(nodeID)) {
Set<String> sessionJIDS = lookupJIDList(nodeID, componentsCache.getName());
sessionJIDS.remove(mapEvent.getKey().toString());
}
}
}
}
}
}
/**
* $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 com.tangosol.net.BackingMapManager;
import com.tangosol.net.DefaultConfigurableCacheFactory;
import com.tangosol.net.MemberListener;
import com.tangosol.net.NamedCache;
import com.tangosol.net.cache.NearCache;
import com.tangosol.net.cache.ReadWriteBackingMap;
import com.tangosol.util.*;
import org.jivesoftware.util.cache.Cache;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
/**
* Clustered implementation of the Cache interface using Tangosol's Coherence product.
*
*/
public class ClusteredCache implements Cache, QueryMap, InvocableMap {
/**
* The map is used for distributed operations such as get, put, etc.
*/
protected NamedCache map;
/**
* The cache is used as the backing store of the main distributed map and
* makes decisions about when to cull or expire cache entries.
*/
private Cache backingCache;
/**
* Create a new cache.
*
* @param name a name for the cache, which should be unique per vm.
*/
protected ClusteredCache(String name) {
NamedCache cache = com.tangosol.net.CacheFactory.getCache(name);
init(name, cache);
}
/**
* 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, NamedCache cache) {
init(name, cache);
}
private void init(String name, NamedCache cache) {
map = cache;
BackingMapManager backingManager = cache.getCacheService().getBackingMapManager();
Map mapBacking = null;
if (backingManager instanceof DefaultConfigurableCacheFactory.Manager) {
DefaultConfigurableCacheFactory.Manager actualManager =
(DefaultConfigurableCacheFactory.Manager) backingManager;
int count = 0;
mapBacking = actualManager.getBackingMap(name);
//this ugly logic is necessary because the backing map instance seems to be made available asynchronously
//by the coherence api
while (mapBacking == null && count < 5) {
// Wait a full second
try {
Thread.sleep(1000);
}
catch (Exception e) { /*ignore*/ }
count++;
mapBacking = actualManager.getBackingMap(name);
}
if (mapBacking instanceof ReadWriteBackingMap) {
ReadWriteBackingMap readWriteMap = (ReadWriteBackingMap)mapBacking;
Map realBackingMap = readWriteMap.getInternalCache();
if (realBackingMap instanceof Cache) {
backingCache = (Cache)realBackingMap;
}
}
else if (mapBacking instanceof Cache) {
backingCache = (Cache)mapBacking;
}
}
if (backingCache == null) {
throw new IllegalStateException("Unable to access backing cache for " + name + ". BackingMapManager is a " +
backingManager.getClass().getName() + " and backing map is " +
((mapBacking != null) ? mapBacking.getClass().getName() : "null")
);
}
backingCache.setName(name);
}
public void addMemberListener(MemberListener listener) {
map.getCacheService().addMemberListener(listener);
}
public void removeMemberListener(MemberListener listener) {
map.getCacheService().removeMemberListener(listener);
}
public void addMapListener(MapListener mapListener, Filter filter, boolean fLite) {
map.addMapListener(mapListener, filter, fLite);
}
public void removeMapListener(MapListener mapListener, Filter filter) {
map.removeMapListener(mapListener, filter);
}
// Cache Interface
public String getName() {
return backingCache.getName();
}
public void setName(String name) {
backingCache.setName(name);
}
public Object put(Object key, Object object) {
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() {
return backingCache.size();
}
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() {
if (map instanceof NearCache) {
return ((NearCache)map).getCacheHits();
}
else if (backingCache != null) {
return backingCache.getCacheHits();
}
else {
return -1;
}
}
public long getCacheMisses() {
if (map instanceof NearCache) {
return ((NearCache)map).getCacheMisses();
}
else if (backingCache != null) {
return backingCache.getCacheMisses();
}
else {
return -1;
}
}
public int getCacheSize() {
return backingCache.getCacheSize();
}
public long getMaxCacheSize() {
return backingCache.getMaxCacheSize();
}
public void setMaxCacheSize(int maxSize) {
backingCache.setMaxCacheSize(maxSize);
}
public long getMaxLifetime() {
return backingCache.getMaxLifetime();
}
public void setMaxLifetime(long maxLifetime) {
backingCache.setMaxLifetime(maxLifetime);
}
public void destroy() {
map.destroy();
}
public boolean lock(Object key, long timeout) {
return map.lock(key, timeout);
}
public boolean unlock(Object key) {
return map.unlock(key);
}
///////// InvocableMap methods //////////////////////////////////
public Object invoke(Object object, EntryProcessor entryProcessor) {
return map.invoke(object, entryProcessor);
}
public Map invokeAll(Collection collection, EntryProcessor entryProcessor) {
return map.invokeAll(collection, entryProcessor);
}
public Map invokeAll(Filter filter, EntryProcessor entryProcessor) {
return map.invokeAll(filter, entryProcessor);
}
public Object aggregate(Collection collection, EntryAggregator entryAggregator) {
return map.aggregate(collection, entryAggregator);
}
public Object aggregate(Filter filter, EntryAggregator entryAggregator) {
return map.aggregate(filter, entryAggregator);
}
////////////// QueryMap methods /////////////////////////
public Set keySet(Filter filter) {
return map.keySet(filter);
}
public Set entrySet(Filter filter) {
return map.entrySet(filter);
}
public Set entrySet(Filter filter, Comparator comparator) {
return map.entrySet(filter, comparator);
}
public void addIndex(ValueExtractor valueExtractor, boolean sorted, Comparator comparator) {
map.addIndex(valueExtractor, sorted, comparator);
}
public void removeIndex(ValueExtractor valueExtractor) {
map.removeIndex(valueExtractor);
}
}
/**
* $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 com.tangosol.net.cache.CacheLoader;
import com.tangosol.net.cache.LocalCache;
import com.tangosol.net.cache.OldCache;
import com.tangosol.util.SafeHashMap;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable;
/**
* Implementation of the Cache interface that uses the Coherence LocalCache class.
*
*/
public class CoherenceCache extends LocalCache implements Cache {
private static final String FLUSH_DELAY_PROP = "cache.local.flushDelay";
private String name = "";
/**
* Default constructor for coherence to create instances.
*/
public CoherenceCache(){
}
public CoherenceCache(int maxSize) {
super(maxSize);
}
public CoherenceCache(int maxSize, int maxLifetime) {
super(maxSize, maxLifetime);
}
public CoherenceCache(int maxSize, int maxLifetime, CacheLoader loader) {
super(maxSize, maxLifetime, loader);
}
/**
* Constructor used only by jive - never called by coherence. If you are creating a subclass, or directly
* instantiating this one, you should only use this constructor, as it does several things not normally required.
* For instance, the low units (low water mark) will be set to 90 percent of the value of <tt>maxSize</tt>. Also,
* if the local jive property <tt>'cache.local.flushDelay'</tt> is set, that value will be used as the number of
* milliseconds passed to {@link #setFlushDelay}. If it is not set, the default value (one minute) will be used.
*
* @param name the name of the cache
* @param maxSize the maximum number of units (bytes, in our case) the cache will hold before evicting older entries.
* @param maxLifetime the time to live (in milliseconds) for entries in the cache.
*/
public CoherenceCache(String name, int maxSize, long maxLifetime) {
super(maxSize<=0?Integer.MAX_VALUE:maxSize, maxLifetime<0?0:(int)maxLifetime);
init(maxSize, name);
}
/**
* Constructor used only by jive - never called by coherence. If you are creating a subclass, or directly
* instantiating this one, you should only use this constructor, as it does several things not normally required.
* For instance, the low units (low water mark) will be set to 90 percent of the value of <tt>maxSize</tt>. Also,
* if the local jive property <tt>'cache.local.flushDelay'</tt> is set, that value will be used as the number of
* milliseconds passed to {@link #setFlushDelay}. If it is not set, the default value (one minute) will be used.
*
* @param name the name of the cache
* @param maxSize the maximum number of units (bytes, in our case) the cache will hold before evicting older entries.
* @param maxLifetime the time to live (in milliseconds) for entries in the cache.
* @param cacheLoader the <tt>CacheLoader</tt> or <tt>CacheStore</tt> to use.
*/
public CoherenceCache(String name, int maxSize, long maxLifetime, CacheLoader cacheLoader) {
super(maxSize<=0?Integer.MAX_VALUE:maxSize, maxLifetime<0?0:(int)maxLifetime, cacheLoader);
init(maxSize, name);
}
private void init(int maxSize, String name) {
if (maxSize > 0) {
setLowUnits((int)(maxSize*.9));
}
String delayProp = JiveGlobals.getProperty(FLUSH_DELAY_PROP);
if (delayProp != null) {
try {
long delay = Long.parseLong(delayProp);
if (delay >=0) {
setFlushDelay((int)delay);
}
}
catch (NumberFormatException nfe) {
Log.warn("Unable to parse " + FLUSH_DELAY_PROP + " using default value of " + delayProp);
}
}
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getMaxCacheSize() {
return getHighUnits();
}
public void setMaxCacheSize(int maxSize) {
setHighUnits(maxSize<=0?Integer.MAX_VALUE:maxSize);
if (maxSize > 0) {
setLowUnits((int)(maxSize*.9));
}
//TODO write these through to coherence configuration
}
public long getMaxLifetime() {
return getExpiryDelay();
}
public void setMaxLifetime(long maxLifetime) {
setExpiryDelay(maxLifetime<0?0:(int)maxLifetime);
//TODO write these through to coherence configuration
}
public int getCacheSize() {
return getUnits();
}
protected SafeHashMap.Entry instantiateEntry() {
return new Entry();
}
/**
* A holder for a cached value.
*/
public class Entry extends OldCache.Entry {
public int calculateUnits(Object object) {
// If the object is Cacheable, ask for its size.
if (object instanceof Cacheable) {
return ((Cacheable) object).getCachedSize();
}
// Coherence puts com.tangosol.util.Binary objects in cache.
else if (object instanceof com.tangosol.util.Binary) {
return ((com.tangosol.util.Binary) object).length();
}
// Check for other common types of objects put into cache.
else if (object instanceof Long) {
return CacheSizes.sizeOfLong();
}
else if (object instanceof Integer) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();
}
else if (object instanceof Boolean) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfBoolean();
}
else if (object instanceof long[]) {
long[] array = (long[]) object;
return CacheSizes.sizeOfObject() + array.length * CacheSizes.sizeOfLong();
}
else if (object instanceof String) {
return CacheSizes.sizeOfString((String)object);
}
else {
return 1;
}
}
}
}
/**
* $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 com.jivesoftware.util.cluster.CoherenceClusterNodeInfo;
import com.tangosol.net.*;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.cluster.ClusterNodeInfo;
import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactoryStrategy;
import org.jivesoftware.util.cache.CacheWrapper;
import org.jivesoftware.util.cache.ClusterTask;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* CacheFactory implementation to use when using Coherence in cluster mode.
*
* @author Gaston Dombiak
*/
public class CoherenceClusteredCacheFactory implements CacheFactoryStrategy {
/**
* Storage for cache statistics
*/
private static Map<String, Map<String, long[]>> cacheStats;
/**
* Maintain a reference to the Coherence Cluster object so that we can get cluster time from
* it.
*/
private static Cluster cluster = null;
/**
* Service used for executing tasks across the cluster
*/
private static InvocationService taskService;
private ClusterListener clusterListener;
/**
* Keeps that running state. Initial state is stopped.
*/
private State state = State.stopped;
public boolean startCluster() {
ClassLoader oldLoader = null;
// Set that we are starting up the cluster service
state = State.starting;
try {
// Store previous class loader (in case we change it)
oldLoader = Thread.currentThread().getContextClassLoader();
// See if the license allows for additional cluster members.
int allowedMembers = getMaxClusterNodes();
// If more than 1 cluster member is allowed...
if (allowedMembers > 1) {
ClassLoader loader = new ClusterClassLoader();
Thread.currentThread().setContextClassLoader(loader);
cluster = com.tangosol.net.CacheFactory.ensureCluster();
// Make sure Coherence uses the correct class loader.
cluster.setContextClassLoader(loader);
int memberCount = cluster.getMemberSet().size();
// See if adding this cluster bumps us over the allowed
// number. If so, shut down the cluster and use local cache.
if (memberCount > allowedMembers) {
com.tangosol.net.CacheFactory.shutdown();
cluster = null;
Log.error("Error joining clustered cache: your "
+ "license only allows for " + allowedMembers +
" cluster nodes. Using local cache instead.");
}
else {
com.tangosol.net.CacheFactory.getCache("opt-$cacheStats");
taskService = com.tangosol.net.CacheFactory.getInvocationService("OpenFire Cluster Service");
// Update the running state of the cluster
state = cluster != null ? State.started : State.stopped;
Member localMember = cluster.getLocalMember();
Member seniorMember = cluster.getOldestMember();
// Set the ID of this cluster node
XMPPServer.getInstance().setNodeID(NodeID.getInstance(getClusterMemberID()));
// Trigger cluster events
ClusterManager.fireJoinedCluster(false);
// CacheFactory is now using clustered caches. We can add our listeners.
clusterListener = new ClusterListener();
taskService.addMemberListener(clusterListener);
if (isSeniorClusterMember()) {
ClusterManager.fireMarkedAsSeniorClusterMember();
}
Log.info("Joining cluster as node: " + localMember.getUid() + ". Senior Member: " +
(localMember == seniorMember ? "YES" : "NO"));
}
}
// Only 1 cluster member is allowed, so use local cache.
else {
Log.error("Error joining clustered cache: your " +
"license only allows for " + allowedMembers +
" cluster nodes. Using local cache instead.");
}
return cluster != null;
}
catch (Exception e) {
Log.error("Unable to start clustering - continuing in local mode", e);
}
finally {
if (oldLoader != null) {
// Restore previous class loader
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
// For some reason the cluster was not started so update the status
state = State.stopped;
return false;
}
public void stopCluster() {
// Stop the cache services.
cacheStats = null;
taskService = null;
// Update the running state of the cluster
state = State.stopped;
// Stop the cluster
com.tangosol.net.CacheFactory.shutdown();
cluster = null;
// Wait until the server has updated its internal state
while (!clusterListener.isDone()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
}
// Reset the node ID
XMPPServer.getInstance().setNodeID(null);
}
public Cache createCache(String name) {
// Check if cluster is being started up
while (state == State.starting) {
// Wait until cluster is fully started (or failed)
try {
Thread.sleep(250);
}
catch (InterruptedException e) {
// Ignore
}
}
if (state == State.stopped) {
throw new IllegalStateException("Cannot create clustered cache when not in a cluster");
}
return new ClusteredCache(name);
}
public void destroyCache(Cache cache) {
if (cache instanceof CacheWrapper) {
cache = ((CacheWrapper)cache).getWrappedCache();
}
ClusteredCache clustered = (ClusteredCache)cache;
clustered.destroy();
}
public boolean isSeniorClusterMember() {
if (taskService != null) {
return taskService.getInfo().getOldestMember().getUid()
.equals(cluster.getLocalMember().getUid());
}
else {
return true;
}
}
public Collection<ClusterNodeInfo> getClusterNodesInfo() {
if (cluster == null) {
return Collections.emptyList();
}
List<ClusterNodeInfo> nodesInfo = new ArrayList<ClusterNodeInfo>();
for (Object member : cluster.getMemberSet()) {
nodesInfo.add(new CoherenceClusterNodeInfo((Member) member));
}
return nodesInfo;
}
public int getMaxClusterNodes() {
// No longer depends on license code so just return a big number
return 10000;
}
public byte[] getSeniorClusterMemberID() {
if (taskService != null) {
return taskService.getInfo().getOldestMember().getUid().toByteArray();
}
else {
return null;
}
}
public byte[] getClusterMemberID() {
if (cluster != null) {
return com.tangosol.net.CacheFactory.getCluster().getLocalMember().getUid().toByteArray();
}
else {
return null;
}
}
public void doClusterTask(final ClusterTask task) {
if (taskService != null) {
Member current = taskService.getCluster().getLocalMember();
Set setMembers = taskService.getInfo().getServiceMembers();
// Don't send to the local instance.
setMembers.remove(current);
// Asynchronously execute the task.
taskService.execute(buildInvocable(task), setMembers, null);
}
}
public boolean doClusterTask(final ClusterTask task, byte[] nodeID) {
if (taskService != null) {
// Get members of the service
Set setMembers = taskService.getInfo().getServiceMembers();
// Remove all members except requested nodeID
for (Iterator it=setMembers.iterator(); it.hasNext();) {
Member member = (Member) it.next();
if (!Arrays.equals(member.getUid().toByteArray(), nodeID)) {
it.remove();
}
}
// Check that the requested member was found
if (!setMembers.isEmpty()) {
// Asynchronously execute the task.
taskService.execute(buildInvocable(task), setMembers, null);
return true;
}
throw new IllegalStateException("Requested node not found in cluster");
}
throw new IllegalStateException("Cluster service is not available");
}
public Collection<Object> doSynchronousClusterTask(ClusterTask task, boolean includeLocalMember) {
if (taskService != null) {
Member current = taskService.getCluster().getLocalMember();
Set setMembers = taskService.getInfo().getServiceMembers();
if (!includeLocalMember) {
// Don't send to the local instance.
setMembers.remove(current);
}
// Execute the task.
Map map = taskService.query(buildInvocable(task), setMembers);
return map != null ? (Collection<Object>)map.values() : Collections.emptyList();
}
else {
return Collections.emptyList();
}
}
public Object doSynchronousClusterTask(ClusterTask task, byte[] nodeID) {
if (taskService != null) {
// Get members of the service
Set setMembers = taskService.getInfo().getServiceMembers();
// Remove all members except requested nodeID
for (Iterator it=setMembers.iterator(); it.hasNext();) {
Member member = (Member) it.next();
if (!Arrays.equals(member.getUid().toByteArray(), nodeID)) {
it.remove();
}
}
// Check that the requested member was found
if (!setMembers.isEmpty()) {
// Asynchronously execute the task.
Map map = taskService.query(buildInvocable(task), setMembers);
return map != null && !map.isEmpty() ? map.values().toArray()[0] : null;
}
throw new IllegalStateException("Requested node not found in cluster");
}
throw new IllegalStateException("Cluster service is not available");
}
public void updateCacheStats(Map<String, Cache> caches) {
if (caches.size() > 0 && cluster != null) {
// Create the cacheStats map if necessary.
if (cacheStats == null) {
cacheStats = (Map<String, Map<String, long[]>>)com.tangosol.net.CacheFactory.getCache("opt-$cacheStats");
}
String uid = cluster.getLocalMember().getUid().toString();
Map<String, long[]> stats = new HashMap<String, long[]>();
for (String cacheName : caches.keySet()) {
Cache cache = caches.get(cacheName);
// The following information is published:
// current size, max size, num elements, cache
// hits, cache misses.
long [] info = new long[5];
info[0] = cache.getCacheSize();
info[1] = cache.getMaxCacheSize();
info[2] = cache.size();
info[3] = cache.getCacheHits();
info[4] = cache.getCacheMisses();
stats.put(cacheName, info);
}
// Publish message
cacheStats.put(uid, stats);
}
}
private static Invocable buildInvocable(final ClusterTask task) {
return new AbstractInvocable() {
public void run() {
task.run();
}
public Object getResult() {
return task.getResult();
}
};
}
public Lock getLock(Object key, Cache cache) {
if (cache instanceof CacheWrapper) {
cache = ((CacheWrapper)cache).getWrappedCache();
}
return new CoherenceLock(key, (ClusteredCache) cache);
}
private static class CoherenceLock implements Lock {
private Object key;
private ClusteredCache cache;
public CoherenceLock(Object key, ClusteredCache cache) {
this.key = key;
this.cache = cache;
}
public void lock() {
cache.lock(key, -1);
}
public void lockInterruptibly() throws InterruptedException {
cache.lock(key, -1);
}
public boolean tryLock() {
return cache.lock(key, 0);
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return cache.lock(key, unit.toMillis(time));
}
public void unlock() {
cache.unlock(key);
}
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
private static enum State {
stopped,
starting,
started
}
}
/**
* $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.util.cache;
import com.tangosol.util.ExternalizableHelper;
import org.jivesoftware.util.cache.ExternalizableUtilStrategy;
import java.io.*;
import java.util.*;
/**
* Serialization strategy that uses Coherence as its underlying mechanism.
*
* @author Gaston Dombiak
*/
public class CoherenceExternalizableUtil implements ExternalizableUtilStrategy {
/**
* Writes a Map of String key and value pairs. This method handles the
* case when the Map is <tt>null</tt>.
*
* @param out the output stream.
* @param stringMap the Map of String key/value pairs.
* @throws java.io.IOException if an error occurs.
*/
public void writeStringMap(DataOutput out, Map<String, String> stringMap) throws IOException {
// Format for map is an int with the number of values,
// followed by all String key/value pairs.
if (stringMap == null) {
out.writeInt(-1);
}
else {
out.writeInt(stringMap.size());
for (Map.Entry<String, String> entry : stringMap.entrySet()) {
ExternalizableHelper.writeSafeUTF(out, entry.getKey());
ExternalizableHelper.writeSafeUTF(out, entry.getValue());
}
}
}
/**
* Reads a Map of String key and value pairs. This method will return
* <tt>null</tt> if the Map written to the stream was <tt>null</tt>.
*
* @param in the input stream.
* @return a Map of String key/value pairs.
* @throws IOException if an error occurs.
*/
public Map<String, String> readStringMap(DataInput in) throws IOException {
// Format for map is an int with the number of values,
// followed by all String key/value pairs.
int propertyCount = in.readInt();
if (propertyCount == -1) {
return null;
}
else {
Map<String, String> stringMap = new HashMap<String, String>();
for (int i = 0; i < propertyCount; i++) {
stringMap.put(ExternalizableHelper.readSafeUTF(in), ExternalizableHelper.readSafeUTF(in));
}
return stringMap;
}
}
/**
* Writes a Map of String key and Set of Strings value pairs. This method DOES NOT handle the
* case when the Map is <tt>null</tt>.
*
* @param out the output stream.
* @param map the Map of String key and Set of Strings value pairs.
* @throws java.io.IOException if an error occurs.
*/
public void writeStringsMap(DataOutput out, Map<String, Set<String>> map) throws IOException {
out.writeInt(map.size());
for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
ExternalizableHelper.writeSafeUTF(out, entry.getKey());
ExternalizableHelper.writeStringArray(out, entry.getValue().toArray(new String[]{}));
}
}
/**
* Reads a Map of String key and Set of Strings value pairs.
*
* @param in the input stream.
* @param map a Map of String key and Set of Strings value pairs.
* @return number of elements added to the collection.
* @throws IOException if an error occurs.
*/
public int readStringsMap(DataInput in, Map<String, Set<String>> map) throws IOException {
// Format for map is an int with the number of values,
// followed by all String key/value pairs.
int propertyCount = in.readInt();
for (int i = 0; i < propertyCount; i++) {
String key = ExternalizableHelper.readSafeUTF(in);
Set<String> value = new HashSet<String>();
Collections.addAll(value, ExternalizableHelper.readStringArray(in));
map.put(key, value);
}
return propertyCount;
}
/**
* Writes a Map of Long key and Integer value pairs. This method handles
* the case when the Map is <tt>null</tt>.
*
* @param out the output stream.
* @param map the Map of Long key/Integer value pairs.
* @throws IOException if an error occurs.
*/
public void writeLongIntMap(DataOutput out, Map<Long, Integer> map) throws IOException {
// Format for map is an int with the number of values,
// followed by all key/value pairs.
if (map == null) {
out.writeInt(-1);
}
else {
out.writeInt(map.size());
for (Map.Entry<Long, Integer> entry : map.entrySet()) {
out.writeLong(entry.getKey());
out.writeInt(entry.getValue());
}
}
}
/**
* Reads a Map of Long key and Integer value pairs. This method will return
* <tt>null</tt> if the Map written to the stream was <tt>null</tt>.
*
* @param in the input stream.
* @return a Map of Long key/Integer value pairs.
* @throws IOException if an error occurs.
*/
public Map readLongIntMap(DataInput in) throws IOException {
// Format for map is an int with the number of values,
// followed by all key/value pairs.
int propertyCount = in.readInt();
if (propertyCount == -1) {
return null;
}
else {
Map<Long, Integer> map = new HashMap<Long, Integer>();
for (int i = 0; i < propertyCount; i++) {
map.put(in.readLong(), in.readInt());
}
return map;
}
}
/**
* Writes a List of Strings. This method handles the case when the List is
* <tt>null</tt>.
*
* @param out the output stream.
* @param stringList the List of Strings.
* @throws IOException if an error occurs.
*/
public void writeStringList(DataOutput out, List stringList) throws IOException {
// Format for list is an int with the number of values,
// followed by all Strings.
if (stringList == null) {
out.writeInt(-1);
}
else {
out.writeInt(stringList.size());
for (int i = 0, n = stringList.size(); i < n; i++) {
ExternalizableHelper.writeSafeUTF(out, (String) stringList.get(i));
}
}
}
/**
* Reads a List of Strings. This method will return <tt>null</tt> if the List
* written to the stream was <tt>null</tt>.
*
* @param in the input stream.
* @return a List of Strings.
* @throws IOException if an error occurs.
*/
public List<String> readStringList(DataInput in) throws IOException {
// Format for list is an int with the number of values,
// followed by all Strings.
int propertyCount = in.readInt();
if (propertyCount == -1) {
return null;
}
else {
List<String> stringList = new ArrayList<String>();
for (int i = 0; i < propertyCount; i++) {
stringList.add(ExternalizableHelper.readSafeUTF(in));
}
return stringList;
}
}
/**
* Writes an array of long values. This method handles the case when the
* array is <tt>null</tt>.
*
* @param out the output stream.
* @param array the array of long values.
* @throws IOException if an error occurs.
*/
public void writeLongArray(DataOutput out, long [] array) throws IOException {
if (array == null) {
ExternalizableHelper.writeInt(out, -1);
return;
}
ExternalizableHelper.writeInt(out, array.length);
for (long item : array) {
ExternalizableHelper.writeLong(out, item);
}
}
/**
* Reads an array of long values. This method will return <tt>null</tt> if
* the array written to the stream was <tt>null</tt>.
*
* @param in the input stream.
* @return an array of long values.
* @throws IOException if an error occurs.
*/
public long [] readLongArray(DataInput in) throws IOException {
int size = ExternalizableHelper.readInt(in);
if (size == -1) {
return null;
}
long [] array = new long[size];
for (int i = 0; i < size; i++) {
array[i] = ExternalizableHelper.readLong(in);
}
return array;
}
public void writeLong(DataOutput out, long value) throws IOException {
ExternalizableHelper.writeLong(out, value);
}
public long readLong(DataInput in) throws IOException {
return ExternalizableHelper.readLong(in);
}
public void writeByteArray(DataOutput out, byte[] value) throws IOException {
ExternalizableHelper.writeByteArray(out, value);
}
public byte[] readByteArray(DataInput in) throws IOException {
return ExternalizableHelper.readByteArray(in);
}
public void writeInt(DataOutput out, int value) throws IOException {
ExternalizableHelper.writeInt(out, value);
}
public int readInt(DataInput in) throws IOException {
return ExternalizableHelper.readInt(in);
}
public void writeBoolean(DataOutput out, boolean value) throws IOException {
out.writeBoolean(value);
}
public boolean readBoolean(DataInput in) throws IOException {
return in.readBoolean();
}
public void writeSerializable(DataOutput out, Serializable value) throws IOException {
ExternalizableHelper.writeSerializable(out, value);
}
public Serializable readSerializable(DataInput in) throws IOException {
return ExternalizableHelper.readSerializable(in);
}
public void writeSafeUTF(DataOutput out, String value) throws IOException {
ExternalizableHelper.writeSafeUTF(out, value);
}
public String readSafeUTF(DataInput in) throws IOException {
return ExternalizableHelper.readSafeUTF(in);
}
public void writeExternalizableCollection(DataOutput out, Collection<? extends Externalizable> value)
throws IOException {
ExternalizableHelper.writeCollection(out, value);
}
public void writeSerializableCollection(DataOutput out, Collection<? extends Serializable> value)
throws IOException {
ExternalizableHelper.writeCollection(out, value);
}
public int readExternalizableCollection(DataInput in, Collection<? extends Externalizable> value,
ClassLoader loader) throws IOException {
return ExternalizableHelper.readCollection(in, value, loader);
}
public int readSerializableCollection(DataInput in, Collection<? extends Serializable> value,
ClassLoader loader) throws IOException {
return ExternalizableHelper.readCollection(in, value, loader);
}
public void writeExternalizableMap(DataOutput out, Map<String, ? extends Externalizable> map) throws IOException {
ExternalizableHelper.writeMap(out, map);
}
public void writeSerializableMap(DataOutput out, Map<String, ? extends Serializable> map) throws IOException {
ExternalizableHelper.writeMap(out, map);
}
public int readExternalizableMap(DataInput in, Map<String, ? extends Externalizable> map, ClassLoader loader) throws IOException {
return ExternalizableHelper.readMap(in, map, loader);
}
public int readSerializableMap(DataInput in, Map<String, ? extends Serializable> map, ClassLoader loader) throws IOException {
return ExternalizableHelper.readMap(in, map, loader);
}
public void writeStrings(DataOutput out, Collection<String> collection) throws IOException {
ExternalizableHelper.writeStringArray(out, collection.toArray(new String[]{}));
}
public int readStrings(DataInput in, Collection<String> collection) throws IOException {
String[] strings = ExternalizableHelper.readStringArray(in);
Collections.addAll(collection, strings);
return strings.length;
}
}
/**
* $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.util.cache;
import com.tangosol.net.DefaultConfigurableCacheFactory;
import com.tangosol.run.xml.XmlElement;
import org.jivesoftware.util.cache.CacheFactory;
import java.util.HashMap;
import java.util.Map;
/**
* A coherence cache factory which adds the ability to register caches at runtime which are not defined
* in the core coherence-cache-config.xml.
*/
public class JiveConfigurableCacheFactory extends DefaultConfigurableCacheFactory
{
public JiveConfigurableCacheFactory() {
super();
}
public JiveConfigurableCacheFactory(String s) {
super(s);
}
public JiveConfigurableCacheFactory(String s, ClassLoader classLoader) {
super(s, classLoader);
}
public JiveConfigurableCacheFactory(XmlElement xmlElement) {
super(xmlElement);
}
public CacheInfo findSchemeMapping(String cacheName) {
CacheInfo mapping = null;
try {
mapping = super.findSchemeMapping(cacheName);
// Check if there are system properties overriding default values
if (CacheFactory.getCacheTypeProperty(cacheName) != null) {
String cacheType = CacheFactory.getCacheTypeProperty(cacheName);
mapping = new CacheInfo(cacheName, cacheType, mapping.getAttributes());
}
if (CacheFactory.hasMaxSizeFromProperty(cacheName)) {
long maxCacheSize = CacheFactory.getMaxCacheSize(cacheName);
if (maxCacheSize == -1l) {
maxCacheSize = 0;
}
mapping.getAttributes().put("back-size-high", Long.toString(maxCacheSize));
}
if (CacheFactory.hasMaxLifetimeFromProperty(cacheName)) {
long maxLifetime = CacheFactory.getMaxCacheLifetime(cacheName);
if (maxLifetime == -1l) {
maxLifetime = 0;
}
mapping.getAttributes().put("back-expiry", Long.toString(maxLifetime));
}
}
catch (IllegalArgumentException iae) {
// Do nothing. Mapping will be null so we will find one later
}
if (mapping == null) {
String typeProperty = CacheFactory.getCacheTypeProperty(cacheName);
long maxCacheSize = CacheFactory.getMaxCacheSize(cacheName);
long minCacheSize = CacheFactory.getMinCacheSize(cacheName);
long maxLifetime = CacheFactory.getMaxCacheLifetime(cacheName);
if (typeProperty != null) {
Map<String, String> attributes = new HashMap<String, String>();
if (maxCacheSize == -1l) {
maxCacheSize = 0;
}
attributes.put("back-size-high", Long.toString(maxCacheSize));
if (maxLifetime == -1l) {
maxLifetime = 0;
}
attributes.put("back-expiry", Long.toString(maxLifetime));
if (minCacheSize == -1l) {
minCacheSize = 0;
}
attributes.put("back-size-low", Long.toString(minCacheSize));
mapping = new CacheInfo(cacheName, typeProperty, attributes);
}
}
if (mapping == null) {
//this is to mirror the superclass behavior
throw new IllegalArgumentException("No scheme for cache: " + cacheName);
}
return mapping;
}
}
\ No newline at end of file
/**
* $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.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;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* 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 com.tangosol.net.Member;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.cluster.ClusterNodeInfo;
import org.jivesoftware.openfire.cluster.NodeID;
/**
* Cluster Node information as provided by Coherence.
*
* @author Gaston Dombiak
*/
public class CoherenceClusterNodeInfo implements ClusterNodeInfo {
private String hostname;
private NodeID nodeID;
private long joinedTime;
private boolean seniorMember;
public CoherenceClusterNodeInfo(Member member) {
hostname = member.getAddress().getHostName();
nodeID = NodeID.getInstance(member.getUid().toByteArray());
joinedTime = member.getTimestamp();
seniorMember = ClusterManager.getSeniorClusterMember().equals(member.getUid().toByteArray());
}
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 com.tangosol.coherence.component.util.SafeCluster;
import com.tangosol.net.AbstractInvocable;
import com.tangosol.net.InvocationService;
import java.io.Serializable;
import java.util.Map;
/**
* A utility class which helps to gather Coherence stats and information.
*/
public class CoherenceInfo {
/**
* CPU time taken in milliseconds.
*/
public static final int STATS_CPU_TIME = 0;
/**
* Packets sent by publisher.
*/
public static final int STATS_SENT = 1;
/**
* Packets resent by publisher.
*/
public static final int STATS_RESENT = 2;
/**
* Packets sent to the reciever.
*/
public static final int STATS_RECEIVED = 1;
/**
* Repeated packets sent to the receiver.
*/
public static final int STATS_REPEATED = 2;
/**
* Returns a Map of CoherenceInfo.NodeInfo objects keyed by Coherence Member objects.
* A NodeInfo object is a collection of various Node stats.
*
* @return a Map of NodeInfo objects.
*/
public static Map getNodeInfo() {
InvocationService service = com.tangosol.net.CacheFactory.getInvocationService("OpenFire Cluster Service");
// Run cluster-wide stats query
Map results = service.query(new AbstractInvocable() {
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();
// Get cluster stats. Get the cluster then get its listeners. From there,
// get more interestig node stats.
com.tangosol.coherence.component.net.Cluster cluster =
(com.tangosol.coherence.component.net.Cluster)
((SafeCluster)com.tangosol.net.CacheFactory.ensureCluster()).getCluster();
//Cluster.PacketPublisher publisher = cluster.getPublisher();
long [] publisherStats = new long[3];
publisherStats[STATS_CPU_TIME] = cluster.getPublisher().getStatsCpu();
publisherStats[STATS_SENT] = cluster.getPublisher().getStatsSent();
publisherStats[STATS_RESENT] = cluster.getPublisher().getStatsResent();
//Cluster.PacketReceiver receiver = cluster.getReceiver();
long [] receiverStats = new long[3];
receiverStats[STATS_CPU_TIME] = -1L;//receiver.getStatsCpu();
receiverStats[STATS_SENT] = cluster.getReceiver().getStatsReceived();
receiverStats[STATS_RESENT] = cluster.getReceiver().getStatsRepeated();
NodeInfo nodeInfo = new NodeInfo(free, total, max, time, publisherStats,
receiverStats);
setResult(nodeInfo);
}
}, null);
return results;
}
/**
* Clears the cache stats.
*/
public static void clearCacheStats() {
InvocationService service = com.tangosol.net.CacheFactory.getInvocationService("OpenFire Cluster Service");
service.execute(new AbstractInvocable() {
public void run() {
com.tangosol.coherence.component.net.Cluster cluster =
(com.tangosol.coherence.component.net.Cluster)
((SafeCluster)com.tangosol.net.CacheFactory.ensureCluster()).getCluster();
cluster.getPublisher().resetStats();
cluster.getReceiver().resetStats();
}
}, null, null);
}
/**
* Encapsulates statistics and information about a cluster node.
*/
public static class NodeInfo implements Serializable {
private long freeMem;
private long totalMem;
private long maxMem;
private long time;
private long [] publisherStats;
private long [] receiverStats;
NodeInfo(long freeMem, long totalMem, long maxMem, long time, long [] publisherStats,
long [] receiverStats)
{
this.freeMem = freeMem;
this.totalMem = totalMem;
this.maxMem = maxMem;
this.time = time;
this.publisherStats = publisherStats;
this.receiverStats = receiverStats;
}
/**
* 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;
}
/**
* Returns statistics about the packet publisher on the node. The following
* stat values are valid array indexes:<ul>
*
* <li> CoherenceInfo.STATS_CPU_TIME
* <li> CoherenceInfo.STATS_SENT
* <li> CoherenceInfo.STATS_RESENT
* </ul>
*
* @return packet publisher stats.
*/
public long [] getPublisherStats() {
return publisherStats;
}
/**
* Returns statistics about the packet receiver on the node. The following
* stat values are valid array indexes:<ul>
*
* <li> Coherence.STATS_CPU_TIME
* <li> CoherenceInfo.STATS_RECEIVED
* <li> CoherenceInfo.STATS_REPEATED</ul>
*
* @return packet reciever stats.
*/
public long [] getReceiverStats() {
return receiverStats;
}
}
}
/**
* $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.Log;
import org.jivesoftware.util.cache.CacheFactory;
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 receipient just left) then an error packet may be created from the remote node and send it
* back to this node.<p>
*
* TODO For optimization reasons, this class instead of sending an InvocationService for each
* packet to route to a remote node may use a smarter logic that would group a few packets into
* a single InvocationService thus reducing network traffic. Moreover, bnux can be used as a way
* to encode packets to send so that XML parsing is optimized on the other side.
*
* @author Gaston Dombiak
*/
public class CoherencePacketRouter implements RemotePacketRouter {
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 receipient
try {
CacheFactory.doClusterTask(new RemotePacketExecution(receipient, packet), nodeID);
return true;
} catch (IllegalStateException e) {
Log.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.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.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* 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() {
// Not used since we are using #execute and not #query when using InvocationService
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
/* alertbox styles (errors and warnings) */
/* global alertbox styles */
.alertbox {
display: block;
position: relative;
-moz-border-radius: 3px;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
width: 735px;
height: auto;
font-size: .85em;
color: #000000;
overflow: hidden;
}
.alertbox h3 {
display: block;
font-size: 1.1em;
margin: 15px 0px 5px 0px;
padding: 0px;
}
.alertbox p {
font-size: .9em;
margin: 3px 0px 10px 0px;
}
.alertbox a {
color: #000000;
}
.alertbox a:hover {
text-decoration: underline;
}
.linkbtn {
display: block;
position: relative;
float: right;
z-index: 100;
top: 10px;
right: 10px;
}
.linkbtn a {
font-weight: bold;
text-decoration: none;
}
.linkbtn a:hover {
text-decoration: underline;
}
.licenseIcon {
display: block;
position: absolute;
width: 22px;
height: 22px;
margin: 12px 12px 20px 12px;
padding: 0px 0px 0px 0px;
}
.licenseIconsmall {
display: block;
position: relative;
float: left;
width: 16px;
height: 16px;
margin: 0px 5px 4px 0px;
padding: 0px 0px 0px 0px;
}
.licenseContent {
display: block;
position: relative;
clear: none;
top: 0px;
left: 50px;
width: 670px;
}
/* alertboxsmall styles */
.alertboxsmall {
display: block;
position: relative;
float: right;
-moz-border-radius-topleft: 3px;
-moz-border-radius-bottomleft: 3px;
padding: 0px 0px 0px 0px;
margin: -12px -18px 0px 0px;
width: 100px;
font-size: .85em;
color: #000000;
}
.alertboxsmall div.licenseContentsmall {
margin: 5px;
line-height: 1.2em;
}
.alertboxsmall div.licenseContentsmall a {
display: block;
font-weight: bold;
text-decoration: none;
color: #A85E00;
padding: 3px 0px 0px 0px;
}
.alertboxsmall div.licenseContentsmall a:hover {
text-decoration: underline;
}
/* error-specific alertbox styles */
.licenseError {
border: 1px solid #BB8888;
background: #EDB9B1 url(../images/certificateimg_error.gif) no-repeat bottom right;
}
.licenseError div.licenseIcon {
background: url(../images/icon_error.gif) no-repeat;
}
.licenseError h3 {
color: #8C0900;
}
.licenseError div.linkbtn a {
color: #8C0900;
}
/* warning-specific alertbox styles */
.licenseWarning {
border: 1px solid #D9B04C;
background: #FFE9B2 url(../images/certificateimg_warning.gif) no-repeat bottom right;
margin-bottom: 10px;
}
.licenseWarning div.licenseIcon {
background: url(../images/icon_warning.gif) no-repeat;
}
.licenseWarning h3 {
color: #CA7303;
}
.licenseWarning div.linkbtn a {
color: #A85E00;
}
.licenseWarningsmall div.licenseIconsmall {
background: url(../images/icon_warning-small.gif) no-repeat;
}
.licenseWarningsmall {
border: 1px solid #D9B04C;
background: #FFE9B2;
}
/* license page specific styles */
.licenseHeader {
display: block;
width: 732px;
overflow: hidden;
border: 1px solid #BBBBBB;
}
.licenseHeader strong {
display: block;
margin: 5px 10px 5px 10px;
}
.licenseBody {
display: block;
width: 732px;
overflow: hidden;
background-color: #FFFFFF;
border: 1px solid #BBBBBB;
border-top: none;
margin-bottom: 25px;
}
.licenseBody table {
width: 90%;
margin: 10px;
}
.licenseBody div.licenseContents {
width: 710px;
margin: 13px;
}
#enterLicenseLink {
display: block;
}
#enterLicenseLink a.licenseLink {
background: url(/images/add-16x16.gif) no-repeat;
padding: 0px 0px 0px 20px;
}
#enterLicense {
display: block;
}
#enterLicense a.cancelLink {
background: url(/images/forbidden-16x16.gif) no-repeat;
padding: 0px 0px 0px 20px;
}
.licenseBody form {
display: block;
width: 680px;
margin: 0px 0px 15px 0px;
padding: 0px;
text-align: right;
}
.licenseBody form textarea {
width: 680px;
height: 185px;
margin-bottom: 5px;
font-family: "Courier New", Courier, monospace;
font-size: 13px;
}
.licenseFormError {
width: 680px;
text-align:left;
color: #8C0900;
font-weight: bold;
padding: 10px 0px 10px 0px;
}
strong.erroritem {
color: #6E1F1F;
}
/* general page elements */
table.settingsTable {
display: block;
border: 1px solid #BBBBBB;
margin: 5px 0px 15px 0px;
}
table.settingsTable thead th {
border-bottom: 1px solid #BBBBBB;
padding: 3px 8px 3px 12px;
font-weight: bold;
text-align: left;
}
table.settingsTable tbody tr td {
padding: 5px 10px 5px 15px;
}
table.settingsTable tbody tr td p {
padding: 10px 0px 5px 0px;
}
table.settingsTable tr {
padding: 0px 0px 10px 0px;
}
/* Permit Client specific styles */
/* modified the fieldset from existing */
/* condensed border to one entry, pulled "width: 95%;" */
fieldset {
display: block;
position: relative;
-moz-border-radius: 3px;
border: 1px solid #CCCCCC;
padding: 2px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
fieldset legend {
color: #000000;
margin-left: 15px;
}
.clientscontent {
display: block;
position: relative;
margin: 0px 15px 0px 15px;
}
.permitclientbox {
display: block;
position: relative;
border: 1px solid #DCDCDC;
-moz-border-radius: 3px;
padding: 3px 2px 2px 2px;
margin: 10px 0px 10px 0px;
width: 600px;
height: auto;
color: #000000;
overflow: hidden;
background-color: #F4F4F4;
line-height: 1.6em;
}
.permitclientbox table tr td {
line-height: 1.8em;
}
.permitclientbox span {
font-size: .85em;
margin-left: 4px;
}
.horizontalrule {
display: block;
height: 1px;
background-color: #DCDCDC;
margin-top: 2px;
margin-bottom: 14px;
overflow: hidden;
clear: both;
}
.permitclientActive {
background-color: #FFFBE2;
}
.specifyclients {
display: block;
position: relative;
margin: 0px 15px 18px 15px;
width: auto;
padding: 0px;
}
/* Reports specific styles */
tr.allreports_report_default td {
padding: 3px 5px 3px 12px;
cursor: pointer;
background-color: #ffffff;
border: none;
border-top: 1px solid #ffffff;
border-bottom: 1px solid #ffffff;
font-size: 12px;
color: #D76C0D;
}
tr.allreports_report_selected td {
padding: 3px 5px 3px 12px;
cursor: pointer;
border: none;
border-top: 1px solid #bbbbbb;
border-bottom: 1px solid #bbbbbb;
background: #edeed7 url('../images/reports_selected-arrow.gif') no-repeat top left;
background-position: 4px 6px;
font-size: 12px;
color: #2f302b;
font-weight: bold;
}
tr.allreports_report_hover td {
padding: 3px 5px 3px 12px;
cursor: pointer;
background-color: #f4f4f4;
border: none;
border-top: 1px solid #bbbbbb;
border-bottom: 1px solid #bbbbbb;
font-size: 12px;
color: #D76C0D;
}
<%@ page import="com.jivesoftware.util.cluster.CoherenceInfo,
com.tangosol.net.Cluster,
com.tangosol.net.Member,
com.tangosol.run.xml.XmlElement,
org.jivesoftware.openfire.cluster.ClusterManager"
%>
<%@ page import="org.jivesoftware.util.*" %>
<%@ page import="org.jivesoftware.util.cache.Cache" %>
<%@ page import="java.text.DecimalFormat" %>
<%@ page import="java.text.NumberFormat" %>
<%@ page import="java.util.*" %>
<%@ page import="java.util.LinkedList" %>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<% webManager.init(request, response, session, application, out ); %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<html>
<head>
<title>Cluster Node Information</title>
<meta name="pageID" content="system-clustering"/>
<meta http-equiv="refresh" content="10" >
<style type="text/css">
.warning {
color : #f00;
font-weight : bold;
}
.jive-stats .jive-table THEAD TH, .jive-stats .jive-table TBODY TD {
border-right : 1px #ccc solid;
text-align : center;
}
.jive-stats .jive-table .c6c7c8, .jive-stats .jive-table .c8, .jive-stats .jive-table TBODY .c8 {
border-right : 0px;
}
.jive-stats .jive-table TBODY TD TABLE TD {
border : 0px;
}
.jive-info .c1 {
width : 30%;
}
.jive-info .c2 {
width : 25%;
}
.jive-info .c3 {
width : 15%;
text-align : center;
}
.jive-info .c4 {
width : 30%;
}
</style>
</head>
<body>
<% // Is clustering enabled? If not, redirect back to the cache page
if (!ClusterManager.isClusteringStarted()) {
response.sendRedirect("../../system-clustering.jsp");
return;
}
// get parameters
boolean clear = request.getParameter("clear") != null;
String nodeID = ParamUtils.getParameter(request, "UID");
// Clear the cache stats if requested
if (clear) {
CoherenceInfo.clearCacheStats();
response.sendRedirect("system-clustering-node.jsp?UID=" + nodeID);
return;
}
// Get the map of node info objs:
Map nodeInfoMap = CoherenceInfo.getNodeInfo();
// List of members
List members = new LinkedList(nodeInfoMap.keySet());
// Sort it according to name
Collections.sort(members, new Comparator<Member>() {
public int compare(Member member1, Member member2) {
String name1 = member1.getAddress().getHostName() + " (" + member1.getId() + ")";
String name2 = member2.getAddress().getHostName() + " (" + member2.getId() + ")";
return name1.toLowerCase().compareTo(name2.toLowerCase().toLowerCase());
}
});
// Get the overall cluster:
Cluster cluster = com.tangosol.net.CacheFactory.ensureCluster();
// Get the cluster's config:
XmlElement clusterConfig = com.tangosol.net.CacheFactory.getClusterConfig();
// If no UID was used, use the UID from the first member in the member list
byte[] byteArray;
if (nodeID == null) {
byteArray = ((Member) members.get(0)).getUid().toByteArray();
} else {
byteArray = Base64.decode(nodeID, Base64.URL_SAFE);
}
// Get the specific member requested
Member member = null;
for (int i = 0; i < members.size(); i++) {
Member m = (Member) members.get(i);
if (Arrays.equals(byteArray, m.getUid().toByteArray())) {
member = m;
break;
}
}
if (member == null) {
Log.warn("Node not found: " + nodeID + " bytearray: " + byteArray);
for (int i = 0; i < members.size(); i++) {
Member m = (Member) members.get(i);
Log.warn("Available members: " + Base64.encodeBytes(m.getUid().toByteArray(), Base64.URL_SAFE) + " bytearray: " + m.getUid().toByteArray());
}
response.sendRedirect("../../system-clustering.jsp");
return;
}
// Get the cache stats object:
Map cacheStats = com.tangosol.net.CacheFactory.getReplicatedCache(
"opt-$cacheStats", com.tangosol.net.CacheFactory.class.getClassLoader());
// Decimal formatter for nubmers
DecimalFormat decFormat = new DecimalFormat("#,##0.0");
NumberFormat numFormat = NumberFormat.getInstance();
DecimalFormat mbFormat = new DecimalFormat("#0.00");
DecimalFormat percentFormat = new DecimalFormat("#0.0");
// Get the list of existing caches
Cache[] caches = webManager.getCaches();
String[] cacheNames = new String[caches.length];
for (int i = 0; i < caches.length; i++) {
cacheNames[i] = caches[i].getName();
}
%>
<p>
Below you will find statistic information for the selected node. This page will be automatically
refreshed every 10 seconds.
</p>
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tr>
<td width="99%">
&nbsp;
</td>
<td width="1%" nowrap="nowrap">
<a href="../../system-clustering.jsp">&laquo; Back to cluster summary</a>
</td>
</tr>
</table>
<br />
<div class="jive-stats">
<div class="jive-table">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th rowspan="2" class="c1">Node</th>
<th rowspan="2" class="c2">Memory Usage</th>
<th colspan="3" class="c3c4c5">Incoming Packets</th>
<th colspan="3" class="c6c7c8">Outgoing Packets</th>
</tr>
<tr>
<th class="c3" colspan="2">Packets Received</th>
<th class="c5">Success</th>
<th class="c6">CPU</th>
<th class="c7">Throughput</th>
<th class="c8">Success</th>
</tr>
</thead>
<tbody>
<% for (int i=0; i<members.size(); i++) {
Member m = (Member)members.get(i);
if (member != m) {
continue;
}
CoherenceInfo.NodeInfo nodeInfo = (CoherenceInfo.NodeInfo)nodeInfoMap.get(m);
long[] incomingStats = nodeInfo.getReceiverStats();
long[] outgoingStats = nodeInfo.getPublisherStats();
int incomingSuccessRate = 100;
if (incomingStats[CoherenceInfo.STATS_RECEIVED] > 0L) {
long repeated = incomingStats[CoherenceInfo.STATS_REPEATED];
long sent = incomingStats[CoherenceInfo.STATS_SENT];
double rate = 1.0 - ((double)repeated/(double)sent);
incomingSuccessRate = (int)Math.round(100.0 * rate);
}
int outgoingSuccessRate = 100;
if (outgoingStats[CoherenceInfo.STATS_RECEIVED] > 0L) {
long repeated = outgoingStats[CoherenceInfo.STATS_REPEATED];
long sent = outgoingStats[CoherenceInfo.STATS_SENT];
double rate = 1.0 - ((double)repeated/(double)sent);
outgoingSuccessRate = (int)Math.round(100.0 * rate);
}
double outgoingThruput = 0.0;
if (outgoingStats[CoherenceInfo.STATS_CPU_TIME] > 0L) {
long sent = outgoingStats[CoherenceInfo.STATS_SENT];
long cpu = outgoingStats[CoherenceInfo.STATS_CPU_TIME];
outgoingThruput = ((double)sent)/((double)cpu);
}
%>
<tr bgcolor="#ffffcc">
<td nowrap class="c1">
<%= m.getAddress().getHostName() %> (<%= m.getId() %>)
</td>
<td class="c2" valign="middle">
<% double freeMem = (double)nodeInfo.getFreeMem()/(1024.0*1024.0);
double maxMem = (double)nodeInfo.getMaxMem()/(1024.0*1024.0);
double totalMem = (double)nodeInfo.getTotalMem()/(1024.0*1024.0);
double usedMem = totalMem - freeMem;
double percentFree = ((maxMem - usedMem)/maxMem)*100.0;
double percentUsed = 100.0 - percentFree;
int percent = 100-(int)Math.round(percentFree);
%>
<table cellpadding="0" cellspacing="0" border="0" width="250">
<tr>
<td width="99%">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border:1px #666 solid;">
<tr>
<% if (percent == 0) { %>
<td width="100%" style="padding:0px;"><img src="../../images/percent-bar-left.gif" width="100%" height="4" border="0" alt=""></td>
<% } else { %>
<% if (percent >= 90) { %>
<td width="<%= percent %>%" background="../../images/percent-bar-used-high.gif" style="padding:0px;"
><img src="images/blank.gif" width="1" height="4" border="0" alt=""></td>
<% } else { %>
<td width="<%= percent %>%" background="../../images/percent-bar-used-low.gif" style="padding:0px;"
><img src="images/blank.gif" width="1" height="4" border="0" alt=""></td>
<% } %>
<td width="<%= (100-percent) %>%" background="../../images/percent-bar-left.gif" style="padding:0px;"
><img src="images/blank.gif" width="1" height="4" border="0" alt=""></td>
<% } %>
</tr>
</table>
</td>
<td width="1%" nowrap="nowrap">
<%= mbFormat.format(totalMem) %> MB, <%= decFormat.format(percentUsed) %>% used
</td>
</tr>
</table>
</td>
<td class="c3" colspan="2">
<%= numFormat.format(incomingStats[CoherenceInfo.STATS_SENT]) %>
</td>
<td class="c5">
<span class="<%= ((incomingSuccessRate < 75) ? "warning" : "") %>">
<%= incomingSuccessRate %>%
</span>
</td>
<td class="c6">
<%= numFormat.format(outgoingStats[CoherenceInfo.STATS_CPU_TIME]) %>ms
</td>
<td class="c7">
<%= decFormat.format(outgoingThruput) %> pack/ms
</td>
<td class="c8">
<span class="<%= ((outgoingSuccessRate < 75) ? "warning" : "") %>">
<%= outgoingSuccessRate %>%
</span>
</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
<br/>
[<a href="system-clustering-node.jsp?clear=true&UID=<%=nodeID%>">Clear Cache Stats</a>]
<br /><br />
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tr>
<td width="1%"><img src="images/server-network-48x48.gif" width="48" height="48" border="0" alt="" hspace="10"></td>
<td width="99%">
<span style="font-size:1.1em;"><b>Node Details: <%= member.getAddress().getHostName() %> (<%= member.getId() %>)</b></span>
<br />
<span style="font-size:0.9em;">
Address: <%= member.getAddress().getHostAddress() %>:<%= member.getPort() %>,
joined: <%= JiveGlobals.formatDateTime(new Date(member.getTimestamp())) %>
</span>
</td>
</tr>
</table>
<p>
Cache statistics for this cluster node appear below.
</p>
<div class="jive-info">
<div class="jive-table">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th class="c1">Cache Type</th>
<th class="c2">Size</th>
<th class="c3">Objects</th>
<th class="c4">Effectiveness</th>
</tr>
</thead>
<tbody>
<% Map cNames = (Map) cacheStats.get(member.getUid().toString());
if (cNames == null) {
%>
<tr>
<td align="center" colspan="4"><i>No stats available</i></td>
</tr>
<% } else {
// Iterate through the cache names,
for (String cacheName : cacheNames) {
long[] theStats = (long[]) cNames.get(cacheName);
// Skip caches that are in this JVM but not in other nodes
if (theStats == null) {
continue;
}
long size = theStats[0];
long maxSize = theStats[1];
long numObjects = theStats[2];
double memUsed = (double) size / (1024 * 1024);
double totalMem = (double) maxSize / (1024 * 1024);
double freeMem = 100 - 100 * memUsed / totalMem;
double usedMem = 100 * memUsed / totalMem;
long hits = theStats[3];
long misses = theStats[4];
double hitPercent = 0.0;
if (hits + misses == 0) {
hitPercent = 0.0;
} else {
hitPercent = 100 * (double) hits / (hits + misses);
}
boolean lowEffec = (hits > 500 && hitPercent < 85.0 && freeMem < 20.0);
%>
<tr>
<td class="c1">
<%= cacheName %>
</td>
<td class="c2">
<% if (maxSize != -1 && maxSize != Integer.MAX_VALUE) { %>
<%= mbFormat.format(totalMem) %> MB,
<%= percentFormat.format(usedMem)%>% used
<% } else { %>
Unlimited
<% } %>
</td>
<td class="c3">
<%= LocaleUtils.getLocalizedNumber(numObjects) %>
</td>
<td class="c4">
<% if (lowEffec) { %>
<font color="#ff0000"><b><%= percentFormat.format(hitPercent)%>%</b>
<% } else { %>
<b><%= percentFormat.format(hitPercent)%>%</b>
<% } %>
(<%= LocaleUtils.getLocalizedNumber(hits) %>
hits, <%= LocaleUtils.getLocalizedNumber(misses) %> misses)
</td>
</tr>
<%
}
}
%>
</tbody>
</table>
</div>
</div>
<br /><br />
<div class="jive-table">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th colspan="2">
Coherence Cluster Details
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="40%">
Coherence Version:
</td>
<td width="60%">
<%= com.tangosol.coherence.component.application.console.Coherence.VERSION %>
</td>
</tr>
<tr>
<td width="40%">
Multicast Address:
</td>
<td width="60%">
<%= clusterConfig.getSafeElement("multicast-listener/address").getString() %>
</td>
</tr>
<tr>
<td width="40%">
Multicast Port:
</td>
<td width="60%">
<%= clusterConfig.getSafeElement("multicast-listener/port").getInt() %>
</td>
</tr>
<tr>
<td width="40%">
Unicast Port:
</td>
<td width="60%">
<%= clusterConfig.getSafeElement("unicast-listener/port").getInt() %>
</td>
</tr>
<tr>
<td width="40%">
Cluster Member Join Timeout:
</td>
<td width="60%">
<%= clusterConfig.getSafeElement("multicast-listener/join-timeout-milliseconds").getString() %> ms
</td>
</tr>
<tr>
<td width="40%">
Packet Size:
</td>
<td width="60%">
<%= clusterConfig.getSafeElement("packet-publisher/packet-size/maximum-length").getInt() %> bytes
</td>
</tr>
</tbody>
</table>
</div>
<br/>
</body>
</html>
\ No newline at end of file
<?xml version='1.0'?>
<!--
Copyright 2001-2005 by Tangosol, Inc. All rights reserved.
This software is the confidential and proprietary information of
Tangosol, Inc. You shall not disclose such confidential and pro-
prietary information and shall use it only in accordance with the
terms of the license agreement you entered into with Tangosol, Inc.
Tangosol, Inc. makes no representations or warranties about the suit-
ability of the software, either express or implied, including but not
limited to the implied warranties of merchantability, fitness for a
particular purpose, or non-infringement. Tangosol, Inc. shall not be
liable for any damages suffered by licensee as a result of using,
modifying or distributing this software or its derivatives.
Tangosol, Inc. is located at http://www.tangosol.com and can be
contacted by e-mail at info@tangosol.com.
This notice may not be removed or altered.
-->
<!--
To override default values defined in this file you will need to add the required
system property as a Java system property.
For instance, if you want to change the default port used for clustering then the
system property to override is tangosol.coherence.clusterport.
Adding -Dtangosol.coherence.clusterport=32390 to your command line when starting
up the server will change the port to use for clustering to be 32390. Depending on
the way you deployed the server you might need to add the property to the
openfired.vmoptions file or /etc/sysconfig/openfire if you are using the RPM install.
-->
<!DOCTYPE coherence PUBLIC
"-//Tangosol, Inc.//DTD Tangosol Coherence 3.0//EN"
"http://www.tangosol.com/dtd/coherence_3_0.dtd">
<coherence>
<cluster-config>
<multicast-listener>
<port system-property="tangosol.coherence.clusterport">32386</port>
<join-timeout-milliseconds>20000</join-timeout-milliseconds>
</multicast-listener>
<shutdown-listener>
<enabled system-property="tangosol.coherence.shutdownhook">false</enabled>
</shutdown-listener>
<services>
<service id="3">
<init-params>
<init-param id="6">
<param-name>backup-count</param-name>
<param-value system-property="tangosol.coherence.distributed.backupcount">1</param-value>
</init-param>
</init-params>
</service>
</services>
</cluster-config>
<configurable-cache-factory-config>
<class-name>com.jivesoftware.util.cache.JiveConfigurableCacheFactory</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value system-property="tangosol.coherence.cacheconfig">coherence-cache-config.xml</param-value>
</init-param>
</init-params>
</configurable-cache-factory-config>
</coherence>
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