iterateStatistics();
-
- /**
- * Return true if profiling is enabled.
- */
- public boolean isProfiling();
-
- /**
- * Set to true to enable profiling.
- *
- * We rely on garbage collection to collect the profiling information. This
- * means there is a unknown delay between when a query is executed and when
- * we actually collect the usage profile information.
- *
- *
- * Due to this garbage collection delay, when turning off profiling while
- * the application is running you should consider calling
- * collectUsageViaGC() BEFORE setProfiling(false). This hints to
- * the JVM to perform garbage collection, and hopefully collects the
- * profiling information.
- *
- */
- public void setProfiling(boolean enable);
-
- /**
- * Return true if automatic query tuning is enabled.
- */
- public boolean isQueryTuning();
-
- /**
- * Set to true to enable automatic query tuning.
- */
- public void setQueryTuning(boolean enable);
-
- /**
- * This controls whether autoFetch is used when it has not been explicitly
- * set on a query via {@link Query#setAutoFetch(boolean)}.
- */
- public AutofetchMode getMode();
-
- /**
- * Set the auto fetch mode used when a query has not had
- * {@link Query#setAutoFetch(boolean)}.
- */
- public void setMode(AutofetchMode Mode);
-
- /**
- * Return the profiling rate (int between 0 and 100).
- */
- public double getProfilingRate();
-
- /**
- * Set the profiling rate (int between 0 and 100).
- */
- public void setProfilingRate(double rate);
-
- /**
- * Return the max number of queries profiled (per query point).
- *
- * The number of queries profiled is collected per query point. Once a query
- * point has profiled this number of queries it does not profile any more.
- *
- */
- public int getProfilingBase();
-
- /**
- * Set a max number of queries to profile per query point.
- *
- * This number should provide a level of confidence that no more profiling
- * is required for this query point.
- *
- */
- public void setProfilingBase(int profilingMax);
-
- /**
- * Return the minimum number of queries profiled before autoFetch will start
- * automatically tuning the queries.
- *
- * This could be one which means start autoFetch tuning after the first
- * profiling information is collected.
- *
- */
- public int getProfilingMin();
-
- /**
- * Set the minimum number of queries profiled per query point before
- * autoFetch will automatically tune the queries.
- *
- * Increasing this number will mean more profiling is collected before
- * autoFetch starts tuning the query.
- *
- */
- public void setProfilingMin(int autoFetchMinThreshold);
-
- /**
- * Fire a garbage collection (hint to the JVM). Assuming garbage collection
- * fires this will gather the usage profiling information.
- */
- public String collectUsageViaGC(long waitMillis);
-
- /**
- * This will take the current profiling information and update the "tuned
- * query detail".
- *
- * This is done periodically and can also be manually invoked.
- *
- *
- * This returns a string summary of the updates that occurred.
- *
- */
- public String updateTunedQueryInfo();
-
- /**
- * Called when a query thinks it should be automatically tuned by autoFetch.
- *
- * This internally checks that autoFetch is enabled, there is a "tuned query
- * detail" to tune the query with and that the autoFetchMinThreshold has
- * been reached.
- *
- *
- * This will also determine if the query should be profiled.
- *
- */
- public boolean tuneQuery(SpiQuery> query);
-
- /**
- * Collect query profiling information.
- *
- * This is for the original query as well as any subsequent lazy loading
- * queries that are required as the object graph is traversed.
- *
- *
- * @param node
- * the node path in the object graph.
- * @param beans
- * the number of beans loaded by the query.
- * @param micros
- * the query executing time in microseconds
- */
- public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
-
-
- /**
- * Return the number of queries tuned by AutoFetch.
- */
- public int getTotalTunedQueryCount();
-
- /**
- * Return the size of the TuneQuery map.
- */
- public int getTotalTunedQuerySize();
-
- /**
- * Return the size of the profile map.
- */
- public int getTotalProfileSize();
-}
+package com.avaje.ebeaninternal.server.autofetch;
+
+import java.util.Iterator;
+
+import com.avaje.ebean.Query;
+import com.avaje.ebean.bean.NodeUsageListener;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.config.AutofetchMode;
+import com.avaje.ebean.config.ServerConfig;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiQuery;
+
+/**
+ * Collects and manages the the profile information.
+ *
+ * The profile information is periodically converted into "tuned query details" -
+ * which is used to automatically tune the queries that use autoFetch.
+ *
+ *
+ * The "tuned query details" effectively are part of the query that has the
+ * select() and join() information (but not the where clause, order by, limits
+ * etc). These are applied to the query when tuneQuery() is called.
+ *
+ */
+public interface AutoFetchManager extends NodeUsageListener {
+
+ /**
+ * Set the owning ebean server.
+ */
+ public void setOwner(SpiEbeanServer server, ServerConfig serverConfig);
+
+ /**
+ * Clear the query execution statistics.
+ */
+ public void clearQueryStatistics();
+
+ /**
+ * Clear all the tuned query info.
+ *
+ * Should only need do this for testing and playing around.
+ *
+ */
+ public int clearTunedQueryInfo();
+
+ /**
+ * Clear all the profiling information.
+ *
+ * This means the profiling information will need to be re-gathered.
+ *
+ *
+ * Should only need do this for testing and playing around.
+ *
+ */
+ public int clearProfilingInfo();
+
+ /**
+ * On shutdown fire garbage collection and collect statistics. Note that
+ * usually we add a little delay (100 milliseconds) to give the garbage
+ * collector plenty of time to do its thing and collect the profile
+ * information.
+ */
+ public void shutdown();
+
+ /**
+ * Return the current tuned fetch information for a given queryPoint key.
+ */
+ public TunedQueryInfo getTunedQueryInfo(String queryPointKey);
+
+ /**
+ * Return the current Statistics for a given queryPoint key.
+ */
+ public Statistics getStatistics(String queryPointKey);
+
+ /**
+ * Iterate the tuned fetch info.
+ *
+ * This should be a read only iteration.
+ *
+ */
+ public Iterator iterateTunedQueryInfo();
+
+ /**
+ * Iterate the node usage statistics.
+ *
+ * This should be a read only iteration.
+ *
+ */
+ public Iterator iterateStatistics();
+
+ /**
+ * Return true if profiling is enabled.
+ */
+ public boolean isProfiling();
+
+ /**
+ * Set to true to enable profiling.
+ *
+ * We rely on garbage collection to collect the profiling information. This
+ * means there is a unknown delay between when a query is executed and when
+ * we actually collect the usage profile information.
+ *
+ *
+ * Due to this garbage collection delay, when turning off profiling while
+ * the application is running you should consider calling
+ * collectUsageViaGC() BEFORE setProfiling(false). This hints to
+ * the JVM to perform garbage collection, and hopefully collects the
+ * profiling information.
+ *
+ */
+ public void setProfiling(boolean enable);
+
+ /**
+ * Return true if automatic query tuning is enabled.
+ */
+ public boolean isQueryTuning();
+
+ /**
+ * Set to true to enable automatic query tuning.
+ */
+ public void setQueryTuning(boolean enable);
+
+ /**
+ * This controls whether autoFetch is used when it has not been explicitly
+ * set on a query via {@link Query#setAutoFetch(boolean)}.
+ */
+ public AutofetchMode getMode();
+
+ /**
+ * Set the auto fetch mode used when a query has not had
+ * {@link Query#setAutoFetch(boolean)}.
+ */
+ public void setMode(AutofetchMode Mode);
+
+ /**
+ * Return the profiling rate (int between 0 and 100).
+ */
+ public double getProfilingRate();
+
+ /**
+ * Set the profiling rate (int between 0 and 100).
+ */
+ public void setProfilingRate(double rate);
+
+ /**
+ * Return the max number of queries profiled (per query point).
+ *
+ * The number of queries profiled is collected per query point. Once a query
+ * point has profiled this number of queries it does not profile any more.
+ *
+ */
+ public int getProfilingBase();
+
+ /**
+ * Set a max number of queries to profile per query point.
+ *
+ * This number should provide a level of confidence that no more profiling
+ * is required for this query point.
+ *
+ */
+ public void setProfilingBase(int profilingMax);
+
+ /**
+ * Return the minimum number of queries profiled before autoFetch will start
+ * automatically tuning the queries.
+ *
+ * This could be one which means start autoFetch tuning after the first
+ * profiling information is collected.
+ *
+ */
+ public int getProfilingMin();
+
+ /**
+ * Set the minimum number of queries profiled per query point before
+ * autoFetch will automatically tune the queries.
+ *
+ * Increasing this number will mean more profiling is collected before
+ * autoFetch starts tuning the query.
+ *
+ */
+ public void setProfilingMin(int autoFetchMinThreshold);
+
+ /**
+ * Fire a garbage collection (hint to the JVM). Assuming garbage collection
+ * fires this will gather the usage profiling information.
+ */
+ public String collectUsageViaGC(long waitMillis);
+
+ /**
+ * This will take the current profiling information and update the "tuned
+ * query detail".
+ *
+ * This is done periodically and can also be manually invoked.
+ *
+ *
+ * This returns a string summary of the updates that occurred.
+ *
+ */
+ public String updateTunedQueryInfo();
+
+ /**
+ * Called when a query thinks it should be automatically tuned by autoFetch.
+ *
+ * This internally checks that autoFetch is enabled, there is a "tuned query
+ * detail" to tune the query with and that the autoFetchMinThreshold has
+ * been reached.
+ *
+ *
+ * This will also determine if the query should be profiled.
+ *
+ */
+ public boolean tuneQuery(SpiQuery> query);
+
+ /**
+ * Collect query profiling information.
+ *
+ * This is for the original query as well as any subsequent lazy loading
+ * queries that are required as the object graph is traversed.
+ *
+ *
+ * @param node
+ * the node path in the object graph.
+ * @param beans
+ * the number of beans loaded by the query.
+ * @param micros
+ * the query executing time in microseconds
+ */
+ public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
+
+
+ /**
+ * Return the number of queries tuned by AutoFetch.
+ */
+ public int getTotalTunedQueryCount();
+
+ /**
+ * Return the size of the TuneQuery map.
+ */
+ public int getTotalTunedQuerySize();
+
+ /**
+ * Return the size of the profile map.
+ */
+ public int getTotalProfileSize();
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java
index d142dd20d..b72997ba7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java
@@ -1,4 +1 @@
-/**
- * Default L2 server cache implementation.
- */
package com.avaje.ebeaninternal.server.cache;
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java
index 0445adbd7..c82d88655 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java
@@ -1,83 +1,64 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-
-/**
- * Represents a relatively small independent message.
- *
- * In general terms we break up a potentially large object like
- * RemoteTransactionEvent into many smaller BinaryMessages. This is so that if
- * they don't all fit on a single Packet we can easily break them up and put
- * them on multiple packets.
- *
- *
- * Also note that for the Multicast approach a Packet will generally contain
- * many messages each directed to different members of the cluster. So it would
- * be common for many Ack, Resend and Control messages to all be contained in a
- * single packet.
- *
- *
- * @author rbygrave
- *
- */
-public class BinaryMessage {
-
- public static final int TYPE_MSGCONTROL = 0;
- public static final int TYPE_BEANIUD = 1;
- public static final int TYPE_TABLEIUD = 2;
- public static final int TYPE_BEANDELTA = 3;
- public static final int TYPE_BEANPATHUPDATE = 4;
- public static final int TYPE_INDEX_INVALIDATE = 6;
- public static final int TYPE_INDEX = 7;
- public static final int TYPE_MSGACK = 8;
- public static final int TYPE_MSGRESEND = 9;
-
- private final ByteArrayOutputStream buffer;
- private final DataOutputStream os;
- private byte[] bytes;
-
- /**
- * Create with an estimated buffer size.
- */
- public BinaryMessage(int bufSize) {
- this.buffer = new ByteArrayOutputStream(bufSize);
- this.os = new DataOutputStream(buffer);
- }
-
- /**
- * Return the DataOutputStream to write content to.
- */
- public DataOutputStream getOs() {
- return os;
- }
-
- /**
- * Return all the content as a byte array.
- */
- public byte[] getByteArray() {
- if (bytes == null) {
- bytes = buffer.toByteArray();
- }
- return bytes;
- }
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+
+/**
+ * Represents a relatively small independent message.
+ *
+ * In general terms we break up a potentially large object like
+ * RemoteTransactionEvent into many smaller BinaryMessages. This is so that if
+ * they don't all fit on a single Packet we can easily break them up and put
+ * them on multiple packets.
+ *
+ *
+ * Also note that for the Multicast approach a Packet will generally contain
+ * many messages each directed to different members of the cluster. So it would
+ * be common for many Ack, Resend and Control messages to all be contained in a
+ * single packet.
+ *
+ *
+ * @author rbygrave
+ *
+ */
+public class BinaryMessage {
+
+ public static final int TYPE_MSGCONTROL = 0;
+ public static final int TYPE_BEANIUD = 1;
+ public static final int TYPE_TABLEIUD = 2;
+ public static final int TYPE_BEANDELTA = 3;
+ public static final int TYPE_BEANPATHUPDATE = 4;
+ public static final int TYPE_INDEX_INVALIDATE = 6;
+ public static final int TYPE_INDEX = 7;
+ public static final int TYPE_MSGACK = 8;
+ public static final int TYPE_MSGRESEND = 9;
+
+ private final ByteArrayOutputStream buffer;
+ private final DataOutputStream os;
+ private byte[] bytes;
+
+ /**
+ * Create with an estimated buffer size.
+ */
+ public BinaryMessage(int bufSize) {
+ this.buffer = new ByteArrayOutputStream(bufSize);
+ this.os = new DataOutputStream(buffer);
+ }
+
+ /**
+ * Return the DataOutputStream to write content to.
+ */
+ public DataOutputStream getOs() {
+ return os;
+ }
+
+ /**
+ * Return all the content as a byte array.
+ */
+ public byte[] getByteArray() {
+ if (bytes == null) {
+ bytes = buffer.toByteArray();
+ }
+ return bytes;
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java
index 64d7d3168..3c0e36fe0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java
@@ -1,42 +1,23 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Holds a List of BinaryMessage's.
- *
- * @author rbygrave
- */
-public class BinaryMessageList {
-
- ArrayList list = new ArrayList();
-
- public void add(BinaryMessage msg) {
- list.add(msg);
- }
-
- public List getList() {
- return list;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds a List of BinaryMessage's.
+ *
+ * @author rbygrave
+ */
+public class BinaryMessageList {
+
+ ArrayList list = new ArrayList();
+
+ public void add(BinaryMessage msg) {
+ list.add(msg);
+ }
+
+ public List getList() {
+ return list;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java
index c184baf91..de871602c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java
@@ -1,45 +1,28 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
-
-
-/**
- * Sends messages to the cluster members.
- */
-public interface ClusterBroadcast {
-
- /**
- * Inform the other cluster members that this instance has come online and
- * start any listeners etc.
- */
- public void startup(ClusterManager clusterManager);
-
- /**
- * Inform the other cluster members that this instance is leaving and
- * shutdown any listeners.
- */
- public void shutdown();
-
- /**
- * Send a transaction event to all the members of the cluster.
- */
- public void broadcast(RemoteTransactionEvent remoteTransEvent);
-
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
+
+
+/**
+ * Sends messages to the cluster members.
+ */
+public interface ClusterBroadcast {
+
+ /**
+ * Inform the other cluster members that this instance has come online and
+ * start any listeners etc.
+ */
+ public void startup(ClusterManager clusterManager);
+
+ /**
+ * Inform the other cluster members that this instance is leaving and
+ * shutdown any listeners.
+ */
+ public void shutdown();
+
+ /**
+ * Send a transaction event to all the members of the cluster.
+ */
+ public void broadcast(RemoteTransactionEvent remoteTransEvent);
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java
index e595a0bda..d5c8651b3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java
@@ -1,122 +1,105 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.config.GlobalProperties;
-import com.avaje.ebeaninternal.api.ClassUtil;
-import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
-import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
-import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
-
-/**
- * Manages the cluster service.
- */
-public class ClusterManager {
-
- private static final Logger logger = Logger.getLogger(ClusterManager.class.getName());
-
- private final ConcurrentHashMap serverMap = new ConcurrentHashMap();
-
- private final Object monitor = new Object();
-
- private final ClusterBroadcast broadcast;
-
- private boolean started;
-
- public ClusterManager() {
-
- String clusterType = GlobalProperties.get("ebean.cluster.type", null);
- if (clusterType == null || clusterType.trim().length() == 0) {
- // not clustering this instance
- this.broadcast = null;
-
- } else {
-
- try {
- if ("mcast".equalsIgnoreCase(clusterType)) {
- this.broadcast = new McastClusterManager();
-
- } else if ("socket".equalsIgnoreCase(clusterType)) {
- this.broadcast = new SocketClusterBroadcast();
-
- } else {
- logger.info("Clustering using [" + clusterType + "]");
- this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
- }
-
- } catch (Exception e) {
- String msg = "Error initialising ClusterManager type [" + clusterType + "]";
- logger.log(Level.SEVERE, msg, e);
- throw new RuntimeException(e);
- }
- }
- }
-
- public void registerServer(EbeanServer server) {
- synchronized (monitor) {
- if (!started) {
- startup();
- }
- serverMap.put(server.getName(), server);
- }
- }
-
- public EbeanServer getServer(String name) {
- synchronized (monitor) {
- return serverMap.get(name);
- }
- }
-
- private void startup() {
- started = true;
- if (broadcast != null) {
- broadcast.startup(this);
- }
- }
-
- /**
- * Return true if clustering is on.
- */
- public boolean isClustering() {
- return broadcast != null;
- }
-
- /**
- * Send the message headers and payload to every server in the cluster.
- */
- public void broadcast(RemoteTransactionEvent remoteTransEvent) {
- if (broadcast != null) {
- broadcast.broadcast(remoteTransEvent);
- }
- }
-
- /**
- * Shutdown the service and Deregister from the cluster.
- */
- public void shutdown() {
- if (broadcast != null) {
- logger.info("ClusterManager shutdown ");
- broadcast.shutdown();
- }
- }
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.avaje.ebean.EbeanServer;
+import com.avaje.ebean.config.GlobalProperties;
+import com.avaje.ebeaninternal.api.ClassUtil;
+import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
+import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
+import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
+
+/**
+ * Manages the cluster service.
+ */
+public class ClusterManager {
+
+ private static final Logger logger = Logger.getLogger(ClusterManager.class.getName());
+
+ private final ConcurrentHashMap serverMap = new ConcurrentHashMap();
+
+ private final Object monitor = new Object();
+
+ private final ClusterBroadcast broadcast;
+
+ private boolean started;
+
+ public ClusterManager() {
+
+ String clusterType = GlobalProperties.get("ebean.cluster.type", null);
+ if (clusterType == null || clusterType.trim().length() == 0) {
+ // not clustering this instance
+ this.broadcast = null;
+
+ } else {
+
+ try {
+ if ("mcast".equalsIgnoreCase(clusterType)) {
+ this.broadcast = new McastClusterManager();
+
+ } else if ("socket".equalsIgnoreCase(clusterType)) {
+ this.broadcast = new SocketClusterBroadcast();
+
+ } else {
+ logger.info("Clustering using [" + clusterType + "]");
+ this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
+ }
+
+ } catch (Exception e) {
+ String msg = "Error initialising ClusterManager type [" + clusterType + "]";
+ logger.log(Level.SEVERE, msg, e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public void registerServer(EbeanServer server) {
+ synchronized (monitor) {
+ if (!started) {
+ startup();
+ }
+ serverMap.put(server.getName(), server);
+ }
+ }
+
+ public EbeanServer getServer(String name) {
+ synchronized (monitor) {
+ return serverMap.get(name);
+ }
+ }
+
+ private void startup() {
+ started = true;
+ if (broadcast != null) {
+ broadcast.startup(this);
+ }
+ }
+
+ /**
+ * Return true if clustering is on.
+ */
+ public boolean isClustering() {
+ return broadcast != null;
+ }
+
+ /**
+ * Send the message headers and payload to every server in the cluster.
+ */
+ public void broadcast(RemoteTransactionEvent remoteTransEvent) {
+ if (broadcast != null) {
+ broadcast.broadcast(remoteTransEvent);
+ }
+ }
+
+ /**
+ * Shutdown the service and Deregister from the cluster.
+ */
+ public void shutdown() {
+ if (broadcast != null) {
+ logger.info("ClusterManager shutdown ");
+ broadcast.shutdown();
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java
index bfb0da2fe..082ccded8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java
@@ -1,43 +1,24 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.io.Serializable;
-
-/**
- * Simple holder of binary data.
- * Used to use Packet based serialisation of RemoteTransactionEvent
- * with simple Java Serialisation of the DataHolder.
- */
-public class DataHolder implements Serializable {
-
- private static final long serialVersionUID = 9090748723571322192L;
-
- private final byte[] data;
-
- public DataHolder(byte[] data) {
- this.data = data;
- }
-
- public byte[] getData() {
- return data;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.io.Serializable;
+
+/**
+ * Simple holder of binary data.
+ * Used to use Packet based serialisation of RemoteTransactionEvent
+ * with simple Java Serialisation of the DataHolder.
+ */
+public class DataHolder implements Serializable {
+
+ private static final long serialVersionUID = 9090748723571322192L;
+
+ private final byte[] data;
+
+ public DataHolder(byte[] data) {
+ this.data = data;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java
index cc33392e1..d8fd083a6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java
@@ -1,212 +1,193 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataInput;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-/**
- * Represents the contents sent as a single DatagramPacket.
- *
- * The contents is typically multiple messages (ACK,PING etc) or all or part of
- * a RemoteTransactionEvent.
- *
- *
- * Due to the hard limit on the size of UDP packets a RemoteTransactionEvent
- * with lots of information could be broken up into multiple packets.
- *
- *
- * @author rbygrave
- */
-public class Packet {
-
- /**
- * A Packet that holds protocol messages like ACK, PING etc.
- */
- public static final short TYPE_MESSAGES = 1;
-
- /**
- * A Packet that holds TransactionEvent information such as Bean
- * and or Table IUD information.
- */
- public static final short TYPE_TRANSEVENT = 2;
-
- /**
- * The type of Packet.
- */
- protected short packetType;
-
- /**
- * The PacketId.
- */
- protected long packetId;
-
- /**
- * The timestamp the Packet was created.
- */
- protected long timestamp;
-
- /**
- * The EbeanServer name this relates to if relevant.
- */
- protected String serverName;
-
- protected ByteArrayOutputStream buffer;
- protected DataOutputStream dataOut;
- protected byte[] bytes;
-
- /**
- * The number of messages in this Packet.
- */
- private int messageCount;
-
- /**
- * The number of times this Packet was resent.
- */
- private int resendCount;
-
- /**
- * Create a Packet for writing messages to.
- */
- public static Packet forWrite(short packetType, long packetId, long timestamp, String serverName) throws IOException {
- return new Packet(true, packetType, packetId, timestamp, serverName);
- }
-
- /**
- * Create a Packet just reading the Header information.
- */
- public static Packet readHeader(DataInput dataInput) throws IOException {
-
- short packetType = dataInput.readShort();
- long packetId = dataInput.readLong();
- long timestamp = dataInput.readLong();
- String serverName = dataInput.readUTF();
-
- return new Packet(false, packetType, packetId, timestamp, serverName);
- }
-
- protected Packet(boolean write, short packetType, long packetId, long timestamp, String serverName) throws IOException{
- this.packetType = packetType;
- this.packetId = packetId;
- this.timestamp = timestamp;
- this.serverName = serverName;
- if (write){
- this.buffer = new ByteArrayOutputStream();
- this.dataOut = new DataOutputStream(buffer);
- writeHeader();
- } else {
- this.buffer = null;
- this.dataOut = null;
- }
- }
-
- private void writeHeader() throws IOException {
- dataOut.writeShort(packetType);
- dataOut.writeLong(packetId);
- dataOut.writeLong(timestamp);
- dataOut.writeUTF(serverName);
- }
-
- public int incrementResendCount() {
- return resendCount++;
- }
-
- public short getPacketType() {
- return packetType;
- }
-
- public long getPacketId() {
- return packetId;
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-
- public String getServerName() {
- return serverName;
- }
-
- public void writeEof() throws IOException {
- dataOut.writeBoolean(false);
- }
-
- public void read(DataInput dataInput) throws IOException {
- boolean more = dataInput.readBoolean();
- while (more){
- int msgType = dataInput.readInt();
- readMessage(dataInput, msgType);
- // see if there is more information
- more = dataInput.readBoolean();
- }
- }
-
- /**
- * Overridden by more specific Packet implementations to read the messages.
- */
- protected void readMessage(DataInput dataInput, int msgType) throws IOException {
-
- }
-
- /**
- * Write a binary message to this packet returning true if there was
- * enough room to do so. Return false if the message was too large for
- * the remaining space left - in this case another Packet should be
- * created to put that message into.
- */
- public boolean writeBinaryMessage(BinaryMessage msg, int maxPacketSize) throws IOException {
-
- byte[] bytes = msg.getByteArray();
-
- if (messageCount > 0 && (bytes.length + buffer.size() > maxPacketSize)){
- // we are actually going to ignore the maxPacketSize iff we have one
- // large message.
-
- // false = no more messages
- dataOut.writeBoolean(false);
- return false;
- }
- ++messageCount;
- // true = another message follows
- dataOut.writeBoolean(true);
- dataOut.write(bytes);
- return true;
- }
-
- public int getSize() {
- return getBytes().length;
- }
-
- /**
- * Return the Packet as raw bytes.
- */
- public byte[] getBytes() {
- if (bytes == null){
- bytes = buffer.toByteArray();
- buffer = null;
- dataOut = null;
- }
- return bytes;
- }
-
-
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Represents the contents sent as a single DatagramPacket.
+ *
+ * The contents is typically multiple messages (ACK,PING etc) or all or part of
+ * a RemoteTransactionEvent.
+ *
+ *
+ * Due to the hard limit on the size of UDP packets a RemoteTransactionEvent
+ * with lots of information could be broken up into multiple packets.
+ *
+ *
+ * @author rbygrave
+ */
+public class Packet {
+
+ /**
+ * A Packet that holds protocol messages like ACK, PING etc.
+ */
+ public static final short TYPE_MESSAGES = 1;
+
+ /**
+ * A Packet that holds TransactionEvent information such as Bean
+ * and or Table IUD information.
+ */
+ public static final short TYPE_TRANSEVENT = 2;
+
+ /**
+ * The type of Packet.
+ */
+ protected short packetType;
+
+ /**
+ * The PacketId.
+ */
+ protected long packetId;
+
+ /**
+ * The timestamp the Packet was created.
+ */
+ protected long timestamp;
+
+ /**
+ * The EbeanServer name this relates to if relevant.
+ */
+ protected String serverName;
+
+ protected ByteArrayOutputStream buffer;
+ protected DataOutputStream dataOut;
+ protected byte[] bytes;
+
+ /**
+ * The number of messages in this Packet.
+ */
+ private int messageCount;
+
+ /**
+ * The number of times this Packet was resent.
+ */
+ private int resendCount;
+
+ /**
+ * Create a Packet for writing messages to.
+ */
+ public static Packet forWrite(short packetType, long packetId, long timestamp, String serverName) throws IOException {
+ return new Packet(true, packetType, packetId, timestamp, serverName);
+ }
+
+ /**
+ * Create a Packet just reading the Header information.
+ */
+ public static Packet readHeader(DataInput dataInput) throws IOException {
+
+ short packetType = dataInput.readShort();
+ long packetId = dataInput.readLong();
+ long timestamp = dataInput.readLong();
+ String serverName = dataInput.readUTF();
+
+ return new Packet(false, packetType, packetId, timestamp, serverName);
+ }
+
+ protected Packet(boolean write, short packetType, long packetId, long timestamp, String serverName) throws IOException{
+ this.packetType = packetType;
+ this.packetId = packetId;
+ this.timestamp = timestamp;
+ this.serverName = serverName;
+ if (write){
+ this.buffer = new ByteArrayOutputStream();
+ this.dataOut = new DataOutputStream(buffer);
+ writeHeader();
+ } else {
+ this.buffer = null;
+ this.dataOut = null;
+ }
+ }
+
+ private void writeHeader() throws IOException {
+ dataOut.writeShort(packetType);
+ dataOut.writeLong(packetId);
+ dataOut.writeLong(timestamp);
+ dataOut.writeUTF(serverName);
+ }
+
+ public int incrementResendCount() {
+ return resendCount++;
+ }
+
+ public short getPacketType() {
+ return packetType;
+ }
+
+ public long getPacketId() {
+ return packetId;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public void writeEof() throws IOException {
+ dataOut.writeBoolean(false);
+ }
+
+ public void read(DataInput dataInput) throws IOException {
+ boolean more = dataInput.readBoolean();
+ while (more){
+ int msgType = dataInput.readInt();
+ readMessage(dataInput, msgType);
+ // see if there is more information
+ more = dataInput.readBoolean();
+ }
+ }
+
+ /**
+ * Overridden by more specific Packet implementations to read the messages.
+ */
+ protected void readMessage(DataInput dataInput, int msgType) throws IOException {
+
+ }
+
+ /**
+ * Write a binary message to this packet returning true if there was
+ * enough room to do so. Return false if the message was too large for
+ * the remaining space left - in this case another Packet should be
+ * created to put that message into.
+ */
+ public boolean writeBinaryMessage(BinaryMessage msg, int maxPacketSize) throws IOException {
+
+ byte[] bytes = msg.getByteArray();
+
+ if (messageCount > 0 && (bytes.length + buffer.size() > maxPacketSize)){
+ // we are actually going to ignore the maxPacketSize iff we have one
+ // large message.
+
+ // false = no more messages
+ dataOut.writeBoolean(false);
+ return false;
+ }
+ ++messageCount;
+ // true = another message follows
+ dataOut.writeBoolean(true);
+ dataOut.write(bytes);
+ return true;
+ }
+
+ public int getSize() {
+ return getBytes().length;
+ }
+
+ /**
+ * Return the Packet as raw bytes.
+ */
+ public byte[] getBytes() {
+ if (bytes == null){
+ bytes = buffer.toByteArray();
+ buffer = null;
+ dataOut = null;
+ }
+ return bytes;
+ }
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java
index 53a182133..9dac59a44 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java
@@ -1,88 +1,69 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.io.DataInput;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebeaninternal.server.cluster.mcast.Message;
-import com.avaje.ebeaninternal.server.cluster.mcast.MessageAck;
-import com.avaje.ebeaninternal.server.cluster.mcast.MessageControl;
-import com.avaje.ebeaninternal.server.cluster.mcast.MessageResend;
-
-/**
- * A Packet that contains Ack, Resend and Control messages.
- *
- * @author rbygrave
- */
-public class PacketMessages extends Packet {
-
- private final ArrayList messages;
-
- public static PacketMessages forWrite(long packetId, long timestamp, String serverName) throws IOException {
- return new PacketMessages(true, packetId, timestamp, serverName);
- }
-
- public static PacketMessages forRead(Packet header) throws IOException {
- return new PacketMessages(header);
- }
-
- private PacketMessages(boolean write, long packetId, long timestamp, String serverName) throws IOException {
- super(write, TYPE_MESSAGES, packetId, timestamp, serverName);
- this.messages = null;
- }
-
- private PacketMessages(Packet header) throws IOException {
- super(false, TYPE_MESSAGES, header.packetId, header.timestamp, header.serverName);
- this.messages = new ArrayList();
- }
-
- /**
- * Return the messages contained in this Packet.
- */
- public List getMessages() {
- return messages;
- }
-
- /**
- * Read the messages (Ack, Resend or Control) contained in this packet.
- */
- protected void readMessage(DataInput dataInput, int msgType) throws IOException {
-
- switch (msgType) {
- case BinaryMessage.TYPE_MSGCONTROL:
- messages.add(MessageControl.readBinaryMessage(dataInput));
- break;
-
- case BinaryMessage.TYPE_MSGACK:
- messages.add(MessageAck.readBinaryMessage(dataInput));
- break;
-
- case BinaryMessage.TYPE_MSGRESEND:
- messages.add(MessageResend.readBinaryMessage(dataInput));
- break;
-
- default:
- throw new RuntimeException("Invalid Transaction msgType "+msgType);
- }
- }
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebeaninternal.server.cluster.mcast.Message;
+import com.avaje.ebeaninternal.server.cluster.mcast.MessageAck;
+import com.avaje.ebeaninternal.server.cluster.mcast.MessageControl;
+import com.avaje.ebeaninternal.server.cluster.mcast.MessageResend;
+
+/**
+ * A Packet that contains Ack, Resend and Control messages.
+ *
+ * @author rbygrave
+ */
+public class PacketMessages extends Packet {
+
+ private final ArrayList messages;
+
+ public static PacketMessages forWrite(long packetId, long timestamp, String serverName) throws IOException {
+ return new PacketMessages(true, packetId, timestamp, serverName);
+ }
+
+ public static PacketMessages forRead(Packet header) throws IOException {
+ return new PacketMessages(header);
+ }
+
+ private PacketMessages(boolean write, long packetId, long timestamp, String serverName) throws IOException {
+ super(write, TYPE_MESSAGES, packetId, timestamp, serverName);
+ this.messages = null;
+ }
+
+ private PacketMessages(Packet header) throws IOException {
+ super(false, TYPE_MESSAGES, header.packetId, header.timestamp, header.serverName);
+ this.messages = new ArrayList();
+ }
+
+ /**
+ * Return the messages contained in this Packet.
+ */
+ public List getMessages() {
+ return messages;
+ }
+
+ /**
+ * Read the messages (Ack, Resend or Control) contained in this packet.
+ */
+ protected void readMessage(DataInput dataInput, int msgType) throws IOException {
+
+ switch (msgType) {
+ case BinaryMessage.TYPE_MSGCONTROL:
+ messages.add(MessageControl.readBinaryMessage(dataInput));
+ break;
+
+ case BinaryMessage.TYPE_MSGACK:
+ messages.add(MessageAck.readBinaryMessage(dataInput));
+ break;
+
+ case BinaryMessage.TYPE_MSGRESEND:
+ messages.add(MessageResend.readBinaryMessage(dataInput));
+ break;
+
+ default:
+ throw new RuntimeException("Invalid Transaction msgType "+msgType);
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java
index a746d28d8..4e1236e40 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java
@@ -1,94 +1,75 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster;
-
-import java.io.DataInput;
-import java.io.IOException;
-
-import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
-import com.avaje.ebeaninternal.server.transaction.BeanDelta;
-import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
-import com.avaje.ebeaninternal.server.transaction.IndexEvent;
-import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
-
-/**
- * A Packet holding TransactionEvent data.
- *
- * Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
- * is actually broken up into smaller messages.
- *
- * @author rbygrave
- */
-public class PacketTransactionEvent extends Packet {
-
- private final SpiEbeanServer server;
-
- private final RemoteTransactionEvent event;
-
- public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException {
- return new PacketTransactionEvent(true, packetId, timestamp, serverName);
- }
-
- private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException {
- super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName);
- this.server = null;
- this.event = null;
- }
-
- private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException {
- super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName);
- this.server = server;
- this.event = new RemoteTransactionEvent(server);
- }
-
- public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException {
- return new PacketTransactionEvent(header, server);
- }
-
- public RemoteTransactionEvent getEvent() {
- return event;
- }
-
- protected void readMessage(DataInput dataInput, int msgType) throws IOException {
-
- switch (msgType) {
- case BinaryMessage.TYPE_BEANIUD:
- event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput));
- break;
-
- case BinaryMessage.TYPE_TABLEIUD:
- event.addTableIUD(TableIUD.readBinaryMessage(dataInput));
- break;
-
- case BinaryMessage.TYPE_BEANDELTA:
- event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
- break;
-
- case BinaryMessage.TYPE_INDEX:
- event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
- break;
-
- default:
- throw new RuntimeException("Invalid Transaction msgType "+msgType);
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
+import com.avaje.ebeaninternal.server.transaction.BeanDelta;
+import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
+import com.avaje.ebeaninternal.server.transaction.IndexEvent;
+import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
+
+/**
+ * A Packet holding TransactionEvent data.
+ *
+ * Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
+ * is actually broken up into smaller messages.
+ *
+ * @author rbygrave
+ */
+public class PacketTransactionEvent extends Packet {
+
+ private final SpiEbeanServer server;
+
+ private final RemoteTransactionEvent event;
+
+ public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException {
+ return new PacketTransactionEvent(true, packetId, timestamp, serverName);
+ }
+
+ private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException {
+ super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName);
+ this.server = null;
+ this.event = null;
+ }
+
+ private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException {
+ super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName);
+ this.server = server;
+ this.event = new RemoteTransactionEvent(server);
+ }
+
+ public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException {
+ return new PacketTransactionEvent(header, server);
+ }
+
+ public RemoteTransactionEvent getEvent() {
+ return event;
+ }
+
+ protected void readMessage(DataInput dataInput, int msgType) throws IOException {
+
+ switch (msgType) {
+ case BinaryMessage.TYPE_BEANIUD:
+ event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput));
+ break;
+
+ case BinaryMessage.TYPE_TABLEIUD:
+ event.addTableIUD(TableIUD.readBinaryMessage(dataInput));
+ break;
+
+ case BinaryMessage.TYPE_BEANDELTA:
+ event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
+ break;
+
+ case BinaryMessage.TYPE_INDEX:
+ event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
+ break;
+
+ default:
+ throw new RuntimeException("Invalid Transaction msgType "+msgType);
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java
index a868fab63..853ec3bc6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java
@@ -1,22 +1,3 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
package com.avaje.ebeaninternal.server.cluster;
import java.io.IOException;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java
index 9e9b14130..9b3d8f57f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java
@@ -1,22 +1,3 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
package com.avaje.ebeaninternal.server.cluster;
import java.io.ByteArrayInputStream;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java
index fa9ef1b84..b35d8e6c5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java
@@ -1,62 +1,43 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Holds a list of ACK and RESEND messages that should be sent out.
- *
- * @author rbygrave
- */
-public class AckResendMessages {
-
- ArrayList messages = new ArrayList();
-
- public String toString() {
- return messages.toString();
- }
-
- public int size() {
- return messages.size();
- }
-
- /**
- * Add a ACK message to send.
- */
- public void add(MessageAck ack){
- messages.add(ack);
- }
-
- /**
- * Add a RESEND message to send.
- */
- public void add(MessageResend resend){
- messages.add(resend);
- }
-
- /**
- * Return all the messages to be sent out.
- */
- public List getMessages() {
- return messages;
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds a list of ACK and RESEND messages that should be sent out.
+ *
+ * @author rbygrave
+ */
+public class AckResendMessages {
+
+ ArrayList messages = new ArrayList();
+
+ public String toString() {
+ return messages.toString();
+ }
+
+ public int size() {
+ return messages.size();
+ }
+
+ /**
+ * Add a ACK message to send.
+ */
+ public void add(MessageAck ack){
+ messages.add(ack);
+ }
+
+ /**
+ * Add a RESEND message to send.
+ */
+ public void add(MessageResend resend){
+ messages.add(resend);
+ }
+
+ /**
+ * Return all the messages to be sent out.
+ */
+ public List getMessages() {
+ return messages;
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java
index 2c47538cf..882e451cf 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java
@@ -1,72 +1,53 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * For this node this holds the ACK gotAllPoint for each member in the cluster.
- *
- * As we receive messages from other members of the cluster periodically we need
- * to send them ACK messages to say we got all the packets up to the gotAllPoint.
- *
- * Thread Safety note: Object only used by McastClusterBroadcast Manager thread.
- * So Single Threaded access.
- *
- * @author rbygrave
- */
-public class IncomingPacketsLastAck {
-
- private HashMap lastAckMap = new HashMap();
-
- public String toString() {
- return lastAckMap.values().toString();
- }
-
- /**
- * Remove a member of the cluster who has left.
- */
- public void remove(String memberHostPort) {
- lastAckMap.remove(memberHostPort);
- }
-
- /**
- * Get the last Ack point for a given member of the cluster.
- */
- public MessageAck getLastAck(String memberHostPort) {
- return lastAckMap.get(memberHostPort);
- }
-
- /**
- * For the ACK messages in AckResendMessages update the
- * last Ack packetId.
- */
- public void updateLastAck(AckResendMessages ackResendMessages) {
- List messages = ackResendMessages.getMessages();
- for (int i = 0; i < messages.size(); i++) {
- Message msg = messages.get(i);
- if (msg instanceof MessageAck){
- MessageAck lastAck = (MessageAck)msg;
- lastAckMap.put(lastAck.getToHostPort(), lastAck);
- }
- }
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * For this node this holds the ACK gotAllPoint for each member in the cluster.
+ *
+ * As we receive messages from other members of the cluster periodically we need
+ * to send them ACK messages to say we got all the packets up to the gotAllPoint.
+ *
+ * Thread Safety note: Object only used by McastClusterBroadcast Manager thread.
+ * So Single Threaded access.
+ *
+ * @author rbygrave
+ */
+public class IncomingPacketsLastAck {
+
+ private HashMap lastAckMap = new HashMap();
+
+ public String toString() {
+ return lastAckMap.values().toString();
+ }
+
+ /**
+ * Remove a member of the cluster who has left.
+ */
+ public void remove(String memberHostPort) {
+ lastAckMap.remove(memberHostPort);
+ }
+
+ /**
+ * Get the last Ack point for a given member of the cluster.
+ */
+ public MessageAck getLastAck(String memberHostPort) {
+ return lastAckMap.get(memberHostPort);
+ }
+
+ /**
+ * For the ACK messages in AckResendMessages update the
+ * last Ack packetId.
+ */
+ public void updateLastAck(AckResendMessages ackResendMessages) {
+ List messages = ackResendMessages.getMessages();
+ for (int i = 0; i < messages.size(); i++) {
+ Message msg = messages.get(i);
+ if (msg instanceof MessageAck){
+ MessageAck lastAck = (MessageAck)msg;
+ lastAckMap.put(lastAck.getToHostPort(), lastAck);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java
index 6d1cad167..3605aad18 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java
@@ -1,292 +1,273 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
-
-/**
- * For Incoming Packets remembers the packets we have received and processed.
- *
- * This determines the gotAllPoint per cluster member and identifies missing
- * packets (gap between gotAllPoint and gotMaxPoint).
- *
- *
- * This information is used by the managerThread so send ACK's for messages we
- * have received and RESEND messages to fill the missing packets we have
- * detected.
- *
- *
- * @author rbygrave
- *
- */
-public class IncomingPacketsProcessed {
-
- private final ConcurrentHashMap mapByMember = new ConcurrentHashMap();
-
- private final int maxResendIncoming;
-
- public IncomingPacketsProcessed(int maxResendIncoming) {
- this.maxResendIncoming = maxResendIncoming;
- }
-
- public void removeMember(String memberKey) {
- mapByMember.remove(memberKey);
- }
-
- /**
- * Return true if we should process this packet. Return false if we have
- * already processed the packet.
- */
- public boolean isProcessPacket(String memberKey, long packetId) {
-
- GotAllPoint memberPackets = getMemberPackets(memberKey);
- return memberPackets.processPacket(packetId);
- }
-
- /**
- * Build the list of ACK and RESEND messages that we should send out
- * to the other members of the cluster.
- */
- public AckResendMessages getAckResendMessages(IncomingPacketsLastAck lastAck) {
-
- // Called by the McastClusterBroadcast manager thread
-
- AckResendMessages response = new AckResendMessages();
-
- for (GotAllPoint member : mapByMember.values()) {
-
- MessageAck lastAckMessage = lastAck.getLastAck(member.getMemberKey());
-
- member.addAckResendMessages(response, lastAckMessage);
- }
-
- return response;
- }
-
- private GotAllPoint getMemberPackets(String memberKey) {
-
- // This method is only called single threaded
- // by the listener thread so I'm happy that this
- // put into mapByMember is ok.
- GotAllPoint memberGotAllPoint = mapByMember.get(memberKey);
- if (memberGotAllPoint == null) {
- memberGotAllPoint = new GotAllPoint(memberKey, maxResendIncoming);
- mapByMember.put(memberKey, memberGotAllPoint);
- }
- return memberGotAllPoint;
- }
-
- /**
- * Keeps track of packets received from a particular member of the cluster.
- *
- * It notes the packetIds of the packets received and uses those to maintain
- * the 'gotAllPoint'. The 'gotAllPoint' is the packetId which we know we
- * received all the previous packets.
- *
- */
- public static class GotAllPoint {
-
- private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName());
-
- private final String memberKey;
- private final int maxResendIncoming;
-
- private long gotAllPoint;
-
- private long gotMaxPoint;
-
- /**
- * Packets received out of order.
- */
- private ArrayList outOfOrderList = new ArrayList();
-
- private HashMap resendCountMap = new HashMap();
-
- public GotAllPoint(String memberKey, int maxResendIncoming) {
- this.memberKey = memberKey;
- this.maxResendIncoming = maxResendIncoming;
- }
-
- /**
- * Add ACK and RESEND messages if required.
- */
- public void addAckResendMessages(AckResendMessages response, MessageAck lastAckMessage) {
-
- synchronized (this) {
- if (lastAckMessage != null && lastAckMessage.getGotAllPacketId() >= gotAllPoint) {
- // nothing has changed
- } else {
- // ACK that we have got every packet up to gotAllPoint
- response.add(new MessageAck(memberKey, gotAllPoint));
- }
-
- if (getMissingPacketCount() > 0) {
- // Ask for these Packets to be RESENT
- List missingPackets = getMissingPackets();
- response.add(new MessageResend(memberKey, missingPackets));
- }
- }
- }
-
- public String getMemberKey() {
- return memberKey;
- }
-
- public long getGotAllPoint() {
- synchronized (this) {
- return gotAllPoint;
- }
- }
-
- public long getGotMaxPoint() {
- synchronized (this) {
- return gotMaxPoint;
- }
- }
-
- private int getMissingPacketCount() {
- if (gotMaxPoint <= gotAllPoint) {
- if (!resendCountMap.isEmpty()) {
- resendCountMap.clear();
- }
- return 0;
- }
- return (int) (gotMaxPoint - gotAllPoint) - outOfOrderList.size();
- }
-
- public List getMissingPackets() {
-
- synchronized (this) {
- ArrayList missingList = new ArrayList();
-
- // this is not particularly efficient but expecting
- // the outOfOrderList to be relatively small
-
- boolean lostPacket = false;
-
- for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) {
- Long packetId = Long.valueOf(i);
- if (!outOfOrderList.contains(packetId)) {
- if (incrementResendCount(packetId)) {
- // request this packet be resent
- missingList.add(packetId);
- } else {
- lostPacket = true;
- }
- }
- }
-
- if (lostPacket){
- checkOutOfOrderList();
- }
-
- return missingList;
- }
- }
-
- /**
- * Return true if this packet has not yet exceeded the maxResendCount.
- */
- private boolean incrementResendCount(Long packetId){
- Integer resendCount = resendCountMap.get(packetId);
- if (resendCount != null){
- int i = resendCount.intValue() + 1;
- if (i > maxResendIncoming){
- // we are going to give up trying to get this packet now
- logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it.");
- resendCountMap.remove(packetId);
- outOfOrderList.add(packetId);
- return false;
- }
- resendCount = Integer.valueOf(i);
- resendCountMap.put(packetId, resendCount);
- } else {
- resendCountMap.put(packetId, ONE);
- }
- return true;
- }
-
- private static final Integer ONE = Integer.valueOf(1);
-
- public boolean processPacket(long packetId) {
- synchronized (this) {
-
- if (gotAllPoint == 0) {
- gotAllPoint = packetId;
- return true;
- }
- if (packetId <= gotAllPoint) {
- // already processed this packet
- return false;
- }
-
- if (!resendCountMap.isEmpty()){
- resendCountMap.remove(Long.valueOf(packetId));
- }
-
- if (packetId == gotAllPoint + 1) {
- gotAllPoint = packetId;
- } else {
- if (packetId > gotMaxPoint) {
- gotMaxPoint = packetId;
- }
- outOfOrderList.add(Long.valueOf(packetId));
- }
- checkOutOfOrderList();
- return true;
- }
- }
-
- private void checkOutOfOrderList() {
-
- if (outOfOrderList.size() == 0) {
- return;
- }
-
- boolean continueCheck;
- do {
- continueCheck = false;
- long nextPoint = gotAllPoint + 1;
-
- Iterator it = outOfOrderList.iterator();
- while (it.hasNext()) {
- Long id = it.next();
- if (id.longValue() == nextPoint) {
- // we found the next one in the outOfOrderList
- it.remove();
- gotAllPoint = nextPoint;
- continueCheck = true;
- break;
- }
- }
- } while (continueCheck);
-
- }
-
- }
-
-
-
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+/**
+ * For Incoming Packets remembers the packets we have received and processed.
+ *
+ * This determines the gotAllPoint per cluster member and identifies missing
+ * packets (gap between gotAllPoint and gotMaxPoint).
+ *
+ *
+ * This information is used by the managerThread so send ACK's for messages we
+ * have received and RESEND messages to fill the missing packets we have
+ * detected.
+ *
+ *
+ * @author rbygrave
+ *
+ */
+public class IncomingPacketsProcessed {
+
+ private final ConcurrentHashMap mapByMember = new ConcurrentHashMap();
+
+ private final int maxResendIncoming;
+
+ public IncomingPacketsProcessed(int maxResendIncoming) {
+ this.maxResendIncoming = maxResendIncoming;
+ }
+
+ public void removeMember(String memberKey) {
+ mapByMember.remove(memberKey);
+ }
+
+ /**
+ * Return true if we should process this packet. Return false if we have
+ * already processed the packet.
+ */
+ public boolean isProcessPacket(String memberKey, long packetId) {
+
+ GotAllPoint memberPackets = getMemberPackets(memberKey);
+ return memberPackets.processPacket(packetId);
+ }
+
+ /**
+ * Build the list of ACK and RESEND messages that we should send out
+ * to the other members of the cluster.
+ */
+ public AckResendMessages getAckResendMessages(IncomingPacketsLastAck lastAck) {
+
+ // Called by the McastClusterBroadcast manager thread
+
+ AckResendMessages response = new AckResendMessages();
+
+ for (GotAllPoint member : mapByMember.values()) {
+
+ MessageAck lastAckMessage = lastAck.getLastAck(member.getMemberKey());
+
+ member.addAckResendMessages(response, lastAckMessage);
+ }
+
+ return response;
+ }
+
+ private GotAllPoint getMemberPackets(String memberKey) {
+
+ // This method is only called single threaded
+ // by the listener thread so I'm happy that this
+ // put into mapByMember is ok.
+ GotAllPoint memberGotAllPoint = mapByMember.get(memberKey);
+ if (memberGotAllPoint == null) {
+ memberGotAllPoint = new GotAllPoint(memberKey, maxResendIncoming);
+ mapByMember.put(memberKey, memberGotAllPoint);
+ }
+ return memberGotAllPoint;
+ }
+
+ /**
+ * Keeps track of packets received from a particular member of the cluster.
+ *
+ * It notes the packetIds of the packets received and uses those to maintain
+ * the 'gotAllPoint'. The 'gotAllPoint' is the packetId which we know we
+ * received all the previous packets.
+ *
+ */
+ public static class GotAllPoint {
+
+ private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName());
+
+ private final String memberKey;
+ private final int maxResendIncoming;
+
+ private long gotAllPoint;
+
+ private long gotMaxPoint;
+
+ /**
+ * Packets received out of order.
+ */
+ private ArrayList outOfOrderList = new ArrayList();
+
+ private HashMap resendCountMap = new HashMap();
+
+ public GotAllPoint(String memberKey, int maxResendIncoming) {
+ this.memberKey = memberKey;
+ this.maxResendIncoming = maxResendIncoming;
+ }
+
+ /**
+ * Add ACK and RESEND messages if required.
+ */
+ public void addAckResendMessages(AckResendMessages response, MessageAck lastAckMessage) {
+
+ synchronized (this) {
+ if (lastAckMessage != null && lastAckMessage.getGotAllPacketId() >= gotAllPoint) {
+ // nothing has changed
+ } else {
+ // ACK that we have got every packet up to gotAllPoint
+ response.add(new MessageAck(memberKey, gotAllPoint));
+ }
+
+ if (getMissingPacketCount() > 0) {
+ // Ask for these Packets to be RESENT
+ List missingPackets = getMissingPackets();
+ response.add(new MessageResend(memberKey, missingPackets));
+ }
+ }
+ }
+
+ public String getMemberKey() {
+ return memberKey;
+ }
+
+ public long getGotAllPoint() {
+ synchronized (this) {
+ return gotAllPoint;
+ }
+ }
+
+ public long getGotMaxPoint() {
+ synchronized (this) {
+ return gotMaxPoint;
+ }
+ }
+
+ private int getMissingPacketCount() {
+ if (gotMaxPoint <= gotAllPoint) {
+ if (!resendCountMap.isEmpty()) {
+ resendCountMap.clear();
+ }
+ return 0;
+ }
+ return (int) (gotMaxPoint - gotAllPoint) - outOfOrderList.size();
+ }
+
+ public List getMissingPackets() {
+
+ synchronized (this) {
+ ArrayList missingList = new ArrayList();
+
+ // this is not particularly efficient but expecting
+ // the outOfOrderList to be relatively small
+
+ boolean lostPacket = false;
+
+ for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) {
+ Long packetId = Long.valueOf(i);
+ if (!outOfOrderList.contains(packetId)) {
+ if (incrementResendCount(packetId)) {
+ // request this packet be resent
+ missingList.add(packetId);
+ } else {
+ lostPacket = true;
+ }
+ }
+ }
+
+ if (lostPacket){
+ checkOutOfOrderList();
+ }
+
+ return missingList;
+ }
+ }
+
+ /**
+ * Return true if this packet has not yet exceeded the maxResendCount.
+ */
+ private boolean incrementResendCount(Long packetId){
+ Integer resendCount = resendCountMap.get(packetId);
+ if (resendCount != null){
+ int i = resendCount.intValue() + 1;
+ if (i > maxResendIncoming){
+ // we are going to give up trying to get this packet now
+ logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it.");
+ resendCountMap.remove(packetId);
+ outOfOrderList.add(packetId);
+ return false;
+ }
+ resendCount = Integer.valueOf(i);
+ resendCountMap.put(packetId, resendCount);
+ } else {
+ resendCountMap.put(packetId, ONE);
+ }
+ return true;
+ }
+
+ private static final Integer ONE = Integer.valueOf(1);
+
+ public boolean processPacket(long packetId) {
+ synchronized (this) {
+
+ if (gotAllPoint == 0) {
+ gotAllPoint = packetId;
+ return true;
+ }
+ if (packetId <= gotAllPoint) {
+ // already processed this packet
+ return false;
+ }
+
+ if (!resendCountMap.isEmpty()){
+ resendCountMap.remove(Long.valueOf(packetId));
+ }
+
+ if (packetId == gotAllPoint + 1) {
+ gotAllPoint = packetId;
+ } else {
+ if (packetId > gotMaxPoint) {
+ gotMaxPoint = packetId;
+ }
+ outOfOrderList.add(Long.valueOf(packetId));
+ }
+ checkOutOfOrderList();
+ return true;
+ }
+ }
+
+ private void checkOutOfOrderList() {
+
+ if (outOfOrderList.size() == 0) {
+ return;
+ }
+
+ boolean continueCheck;
+ do {
+ continueCheck = false;
+ long nextPoint = gotAllPoint + 1;
+
+ Iterator it = outOfOrderList.iterator();
+ while (it.hasNext()) {
+ Long id = it.next();
+ if (id.longValue() == nextPoint) {
+ // we found the next one in the outOfOrderList
+ it.remove();
+ gotAllPoint = nextPoint;
+ continueCheck = true;
+ break;
+ }
+ }
+ } while (continueCheck);
+
+ }
+
+ }
+
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java
index 4fef7370b..314db9ce4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java
@@ -1,22 +1,3 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java
index ec67b2f4e..5c3122111 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java
@@ -1,22 +1,3 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java
index a9830ecaf..c52adc3c0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java
@@ -1,135 +1,116 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.avaje.ebeaninternal.server.cluster.Packet;
-
-/**
- * Handles the sending of Packets via DatagramPacket.
- *
- * @author rbygrave
- */
-public class McastSender {
-
- private static final Logger logger = Logger.getLogger(McastSender.class.getName());
-
- private final int port;
-
- private final InetAddress inetAddress;
-
- private final DatagramSocket sock;
-
- private final InetSocketAddress sendAddr;
-
- private final String senderHostPort;
-
-
- public McastSender(int port, String address, int sendPort, String sendAddress) {
-
- try {
- this.port = port;
- this.inetAddress = InetAddress.getByName(address);
-
- InetAddress sendInetAddress = null;
- if (sendAddress != null) {
- sendInetAddress = InetAddress.getByName(sendAddress);
- } else {
- sendInetAddress = InetAddress.getLocalHost();
- }
-
- if (sendPort > 0) {
- this.sock = new DatagramSocket(sendPort, sendInetAddress);
- } else {
- this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0));
- }
-
- String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]";
- logger.info(msg);
-
- this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort());
- this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort();
-
- } catch (Exception e) {
- String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address;
- throw new RuntimeException(msg, e);
- }
- }
-
- /**
- * Return the send Address so that if we have loopback messages we can
- * detect if they where sent by this local sender and hence should be
- * ignored.
- */
- public InetSocketAddress getAddress() {
- return sendAddr;
- }
-
- /**
- * Return the Host and Port of the sender. This is used to uniquely identify
- * this instance in the cluster.
- */
- public String getSenderHostPort() {
- return senderHostPort;
- }
-
- /**
- * Send the packet.
- */
- public int sendPacket(Packet packet) throws IOException {
-
- byte[] pktBytes = packet.getBytes();
-
- if (logger.isLoggable(Level.FINE)){
- logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length);
- }
-
- if (pktBytes.length > 65507){
- logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length
- +" likely to be truncated using UDP with a MAXIMUM length of 65507");
- }
-
- DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port);
- sock.send(pack);
-
- return pktBytes.length;
- }
-
- /**
- * Send the list of Packets.
- */
- public int sendPackets(List packets) throws IOException {
-
- int totalBytes = 0;
- for (int i = 0; i < packets.size(); i++) {
- totalBytes += sendPacket(packets.get(i));
- }
- return totalBytes;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.avaje.ebeaninternal.server.cluster.Packet;
+
+/**
+ * Handles the sending of Packets via DatagramPacket.
+ *
+ * @author rbygrave
+ */
+public class McastSender {
+
+ private static final Logger logger = Logger.getLogger(McastSender.class.getName());
+
+ private final int port;
+
+ private final InetAddress inetAddress;
+
+ private final DatagramSocket sock;
+
+ private final InetSocketAddress sendAddr;
+
+ private final String senderHostPort;
+
+
+ public McastSender(int port, String address, int sendPort, String sendAddress) {
+
+ try {
+ this.port = port;
+ this.inetAddress = InetAddress.getByName(address);
+
+ InetAddress sendInetAddress = null;
+ if (sendAddress != null) {
+ sendInetAddress = InetAddress.getByName(sendAddress);
+ } else {
+ sendInetAddress = InetAddress.getLocalHost();
+ }
+
+ if (sendPort > 0) {
+ this.sock = new DatagramSocket(sendPort, sendInetAddress);
+ } else {
+ this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0));
+ }
+
+ String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]";
+ logger.info(msg);
+
+ this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort());
+ this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort();
+
+ } catch (Exception e) {
+ String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address;
+ throw new RuntimeException(msg, e);
+ }
+ }
+
+ /**
+ * Return the send Address so that if we have loopback messages we can
+ * detect if they where sent by this local sender and hence should be
+ * ignored.
+ */
+ public InetSocketAddress getAddress() {
+ return sendAddr;
+ }
+
+ /**
+ * Return the Host and Port of the sender. This is used to uniquely identify
+ * this instance in the cluster.
+ */
+ public String getSenderHostPort() {
+ return senderHostPort;
+ }
+
+ /**
+ * Send the packet.
+ */
+ public int sendPacket(Packet packet) throws IOException {
+
+ byte[] pktBytes = packet.getBytes();
+
+ if (logger.isLoggable(Level.FINE)){
+ logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length);
+ }
+
+ if (pktBytes.length > 65507){
+ logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length
+ +" likely to be truncated using UDP with a MAXIMUM length of 65507");
+ }
+
+ DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port);
+ sock.send(pack);
+
+ return pktBytes.length;
+ }
+
+ /**
+ * Send the list of Packets.
+ */
+ public int sendPackets(List packets) throws IOException {
+
+ int totalBytes = 0;
+ for (int i = 0; i < packets.size(); i++) {
+ totalBytes += sendPacket(packets.get(i));
+ }
+ return totalBytes;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java
index 472512e59..bc5c8e8db 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java
@@ -1,155 +1,136 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-/**
- * Gives an overall status of this Cluster instance.
- *
- * Ideally you want to see relatively low Re-send statistics.
- *
- *
- * @author rbygrave
- *
- */
-public class McastStatus {
-
- private final long totalTxnEventsSent;
- private final long totalTxnEventsReceived;
-
- private final long totalPacketsSent;
- private final long totalPacketsResent;
- private final long totalPacketsReceived;
-
- private final long totalBytesSent;
- private final long totalBytesResent;
- private final long totalBytesReceived;
-
- private final int currentGroupSize;
- private final int outgoingPacketsCacheSize;
-
- private final long currentPacketId;
- private final long minAckedPacketId;
- private final String lastOutgoingAcks;
-
- public String getSummary() {
-
- StringBuilder sb = new StringBuilder(80);
- sb.append("txnOut:").append(totalTxnEventsSent).append("; ");
- sb.append("txnIn:").append(totalTxnEventsReceived).append("; ");
- sb.append("outPackets:").append(totalPacketsSent).append("; ");
- sb.append("outBytes:").append(totalBytesSent).append("; ");
- sb.append("inPackets:").append(totalPacketsReceived).append("; ");
- sb.append("inBytes:").append(totalBytesReceived).append("; ");
- sb.append("resentPackets:").append(totalPacketsResent).append("; ");
- sb.append("resentBytes:").append(totalBytesResent).append("; ");
- sb.append("groupSize:").append(currentGroupSize).append("; ");
- sb.append("cache:").append(outgoingPacketsCacheSize).append("; ");
- sb.append("currentPacket:").append(currentPacketId).append("; ");
- sb.append("minAckedPacket:").append(minAckedPacketId).append("; ");
- sb.append("lastAck:").append(lastOutgoingAcks).append("; ");
-
- return sb.toString();
- }
-
- public McastStatus(int currentGroupSize,
- int outgoingPacketsCacheSize,
- long currentPacketId,
- long minAckedPacketId,
- String lastOutgoingAcks,
- long totalTransEventsSent,
- long totalTransEventsReceived,
- long totalPacketsSent,
- long totalPacketsResent,
- long totalPacketsReceived,
- long totalBytesSent,
- long totalBytesResent,
- long totalBytesReceived) {
-
- this.currentGroupSize = currentGroupSize;
- this.outgoingPacketsCacheSize = outgoingPacketsCacheSize;
- this.currentPacketId = currentPacketId;
- this.minAckedPacketId = minAckedPacketId;
- this.lastOutgoingAcks = lastOutgoingAcks;
- this.totalTxnEventsSent = totalTransEventsSent;
- this.totalTxnEventsReceived = totalTransEventsReceived;
- this.totalPacketsSent = totalPacketsSent;
- this.totalPacketsResent = totalPacketsResent;
- this.totalPacketsReceived = totalPacketsReceived;
-
- this.totalBytesSent = totalBytesSent;
- this.totalBytesResent = totalBytesResent;
- this.totalBytesReceived = totalBytesReceived;
-
- }
-
-
- public long getTotalTxnEventsReceived() {
- return totalTxnEventsReceived;
- }
-
- public long getTotalPacketsReceived() {
- return totalPacketsReceived;
- }
-
- public long getTotalBytesSent() {
- return totalBytesSent;
- }
-
- public long getTotalBytesResent() {
- return totalBytesResent;
- }
-
- public long getTotalBytesReceived() {
- return totalBytesReceived;
- }
-
- public String getLastOutgoingAcks() {
- return lastOutgoingAcks;
- }
-
- public int getOutgoingPacketsCacheSize() {
- return outgoingPacketsCacheSize;
- }
-
- public long getCurrentPacketId() {
- return currentPacketId;
- }
-
- public long getMinAckedPacketId() {
- return minAckedPacketId;
- }
-
- public long getTotalTxnEventsSent() {
- return totalTxnEventsSent;
- }
-
- public long getTotalPacketsSent() {
- return totalPacketsSent;
- }
-
- public long getTotalPacketsResent() {
- return totalPacketsResent;
- }
-
- public long getCurrentGroupSize() {
- return currentGroupSize;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+/**
+ * Gives an overall status of this Cluster instance.
+ *
+ * Ideally you want to see relatively low Re-send statistics.
+ *
+ *
+ * @author rbygrave
+ *
+ */
+public class McastStatus {
+
+ private final long totalTxnEventsSent;
+ private final long totalTxnEventsReceived;
+
+ private final long totalPacketsSent;
+ private final long totalPacketsResent;
+ private final long totalPacketsReceived;
+
+ private final long totalBytesSent;
+ private final long totalBytesResent;
+ private final long totalBytesReceived;
+
+ private final int currentGroupSize;
+ private final int outgoingPacketsCacheSize;
+
+ private final long currentPacketId;
+ private final long minAckedPacketId;
+ private final String lastOutgoingAcks;
+
+ public String getSummary() {
+
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("txnOut:").append(totalTxnEventsSent).append("; ");
+ sb.append("txnIn:").append(totalTxnEventsReceived).append("; ");
+ sb.append("outPackets:").append(totalPacketsSent).append("; ");
+ sb.append("outBytes:").append(totalBytesSent).append("; ");
+ sb.append("inPackets:").append(totalPacketsReceived).append("; ");
+ sb.append("inBytes:").append(totalBytesReceived).append("; ");
+ sb.append("resentPackets:").append(totalPacketsResent).append("; ");
+ sb.append("resentBytes:").append(totalBytesResent).append("; ");
+ sb.append("groupSize:").append(currentGroupSize).append("; ");
+ sb.append("cache:").append(outgoingPacketsCacheSize).append("; ");
+ sb.append("currentPacket:").append(currentPacketId).append("; ");
+ sb.append("minAckedPacket:").append(minAckedPacketId).append("; ");
+ sb.append("lastAck:").append(lastOutgoingAcks).append("; ");
+
+ return sb.toString();
+ }
+
+ public McastStatus(int currentGroupSize,
+ int outgoingPacketsCacheSize,
+ long currentPacketId,
+ long minAckedPacketId,
+ String lastOutgoingAcks,
+ long totalTransEventsSent,
+ long totalTransEventsReceived,
+ long totalPacketsSent,
+ long totalPacketsResent,
+ long totalPacketsReceived,
+ long totalBytesSent,
+ long totalBytesResent,
+ long totalBytesReceived) {
+
+ this.currentGroupSize = currentGroupSize;
+ this.outgoingPacketsCacheSize = outgoingPacketsCacheSize;
+ this.currentPacketId = currentPacketId;
+ this.minAckedPacketId = minAckedPacketId;
+ this.lastOutgoingAcks = lastOutgoingAcks;
+ this.totalTxnEventsSent = totalTransEventsSent;
+ this.totalTxnEventsReceived = totalTransEventsReceived;
+ this.totalPacketsSent = totalPacketsSent;
+ this.totalPacketsResent = totalPacketsResent;
+ this.totalPacketsReceived = totalPacketsReceived;
+
+ this.totalBytesSent = totalBytesSent;
+ this.totalBytesResent = totalBytesResent;
+ this.totalBytesReceived = totalBytesReceived;
+
+ }
+
+
+ public long getTotalTxnEventsReceived() {
+ return totalTxnEventsReceived;
+ }
+
+ public long getTotalPacketsReceived() {
+ return totalPacketsReceived;
+ }
+
+ public long getTotalBytesSent() {
+ return totalBytesSent;
+ }
+
+ public long getTotalBytesResent() {
+ return totalBytesResent;
+ }
+
+ public long getTotalBytesReceived() {
+ return totalBytesReceived;
+ }
+
+ public String getLastOutgoingAcks() {
+ return lastOutgoingAcks;
+ }
+
+ public int getOutgoingPacketsCacheSize() {
+ return outgoingPacketsCacheSize;
+ }
+
+ public long getCurrentPacketId() {
+ return currentPacketId;
+ }
+
+ public long getMinAckedPacketId() {
+ return minAckedPacketId;
+ }
+
+ public long getTotalTxnEventsSent() {
+ return totalTxnEventsSent;
+ }
+
+ public long getTotalPacketsSent() {
+ return totalPacketsSent;
+ }
+
+ public long getTotalPacketsResent() {
+ return totalPacketsResent;
+ }
+
+ public long getCurrentGroupSize() {
+ return currentGroupSize;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java
index e44775489..f6a2fc5b6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java
@@ -1,33 +1,14 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.io.IOException;
-
-import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
-
-public interface Message {
-
- public void writeBinaryMessage(BinaryMessageList msgList) throws IOException;
-
- public boolean isControlMessage();
-
- public String getToHostPort();
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.io.IOException;
+
+import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
+
+public interface Message {
+
+ public void writeBinaryMessage(BinaryMessageList msgList) throws IOException;
+
+ public boolean isControlMessage();
+
+ public String getToHostPort();
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java
index 1eaca1a7f..6266c0dd5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java
@@ -1,76 +1,57 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.io.DataInput;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
-import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
-
-public class MessageAck implements Message {
-
- private final String toHostPort;
-
- private final long gotAllPacketId;
-
- public MessageAck(String toHostPort, long gotAllPacketId) {
- this.toHostPort = toHostPort;
- this.gotAllPacketId = gotAllPacketId;
- }
-
- public String toString() {
- return "Ack "+toHostPort+" "+gotAllPacketId;
- }
-
- public boolean isControlMessage() {
- return false;
- }
-
- public String getToHostPort() {
- return toHostPort;
- }
-
- public long getGotAllPacketId() {
- return gotAllPacketId;
- }
-
-
- public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException {
-
- String hostPort = dataInput.readUTF();
- long gotAllPacketId = dataInput.readLong();
- return new MessageAck(hostPort, gotAllPacketId);
- }
-
- public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
-
- BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
-
- DataOutputStream os = m.getOs();
- os.writeInt(BinaryMessage.TYPE_MSGACK);
- os.writeUTF(toHostPort);
- os.writeLong(gotAllPacketId);
- os.flush();
-
- msgList.add(m);
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.io.DataInput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
+import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
+
+public class MessageAck implements Message {
+
+ private final String toHostPort;
+
+ private final long gotAllPacketId;
+
+ public MessageAck(String toHostPort, long gotAllPacketId) {
+ this.toHostPort = toHostPort;
+ this.gotAllPacketId = gotAllPacketId;
+ }
+
+ public String toString() {
+ return "Ack "+toHostPort+" "+gotAllPacketId;
+ }
+
+ public boolean isControlMessage() {
+ return false;
+ }
+
+ public String getToHostPort() {
+ return toHostPort;
+ }
+
+ public long getGotAllPacketId() {
+ return gotAllPacketId;
+ }
+
+
+ public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException {
+
+ String hostPort = dataInput.readUTF();
+ long gotAllPacketId = dataInput.readLong();
+ return new MessageAck(hostPort, gotAllPacketId);
+ }
+
+ public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
+
+ BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
+
+ DataOutputStream os = m.getOs();
+ os.writeInt(BinaryMessage.TYPE_MSGACK);
+ os.writeUTF(toHostPort);
+ os.writeLong(gotAllPacketId);
+ os.flush();
+
+ msgList.add(m);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java
index e455db0ee..d5494d3aa 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java
@@ -1,92 +1,73 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.io.DataInput;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
-import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
-
-public class MessageControl implements Message {
-
- public static final short TYPE_JOIN = 1;
- public static final short TYPE_LEAVE = 2;
- public static final short TYPE_PING = 3;
- public static final short TYPE_JOINRESPONSE = 7;
- public static final short TYPE_PINGRESPONSE = 8;
-
- private final short controlType;
- private final String fromHostPort;
-
- public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException {
- short controlType = dataInput.readShort();
- String hostPort = dataInput.readUTF();
- return new MessageControl(controlType, hostPort);
- }
-
- public MessageControl(short controlType, String helloFromHostPort) {
- this.controlType = controlType;
- this.fromHostPort = helloFromHostPort;
- }
-
-
- public String toString() {
- switch (controlType) {
- case TYPE_JOIN: return "Join "+fromHostPort;
- case TYPE_LEAVE: return "Leave "+fromHostPort;
- case TYPE_PING: return "Ping "+fromHostPort;
- case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort;
-
- default:
- throw new RuntimeException("Invalid controlType "+controlType);
- }
- }
-
- public boolean isControlMessage() {
- return true;
- }
-
- public short getControlType() {
- return controlType;
- }
-
- public String getToHostPort() {
- return "*";
- }
-
- public String getFromHostPort() {
- return fromHostPort;
- }
-
- public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
-
- BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10);
-
- DataOutputStream os = m.getOs();
- os.writeInt(BinaryMessage.TYPE_MSGCONTROL);
- os.writeShort(controlType);
- os.writeUTF(fromHostPort);
- os.flush();
-
- msgList.add(m);
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.io.DataInput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
+import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
+
+public class MessageControl implements Message {
+
+ public static final short TYPE_JOIN = 1;
+ public static final short TYPE_LEAVE = 2;
+ public static final short TYPE_PING = 3;
+ public static final short TYPE_JOINRESPONSE = 7;
+ public static final short TYPE_PINGRESPONSE = 8;
+
+ private final short controlType;
+ private final String fromHostPort;
+
+ public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException {
+ short controlType = dataInput.readShort();
+ String hostPort = dataInput.readUTF();
+ return new MessageControl(controlType, hostPort);
+ }
+
+ public MessageControl(short controlType, String helloFromHostPort) {
+ this.controlType = controlType;
+ this.fromHostPort = helloFromHostPort;
+ }
+
+
+ public String toString() {
+ switch (controlType) {
+ case TYPE_JOIN: return "Join "+fromHostPort;
+ case TYPE_LEAVE: return "Leave "+fromHostPort;
+ case TYPE_PING: return "Ping "+fromHostPort;
+ case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort;
+
+ default:
+ throw new RuntimeException("Invalid controlType "+controlType);
+ }
+ }
+
+ public boolean isControlMessage() {
+ return true;
+ }
+
+ public short getControlType() {
+ return controlType;
+ }
+
+ public String getToHostPort() {
+ return "*";
+ }
+
+ public String getFromHostPort() {
+ return fromHostPort;
+ }
+
+ public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
+
+ BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10);
+
+ DataOutputStream os = m.getOs();
+ os.writeInt(BinaryMessage.TYPE_MSGCONTROL);
+ os.writeShort(controlType);
+ os.writeUTF(fromHostPort);
+ os.flush();
+
+ msgList.add(m);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java
index 80f8f5f77..700c8a2a3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java
@@ -1,96 +1,77 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.io.DataInput;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
-import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
-
-public class MessageResend implements Message {
-
- private final String toHostPort;
-
- private final List resendPacketIds;
-
- public MessageResend(String toHostPort, List resendPacketIds) {
- this.toHostPort = toHostPort;
- this.resendPacketIds = resendPacketIds;
- }
-
- public MessageResend(String toHostPort) {
- this(toHostPort, new ArrayList(4));
- }
-
- public String toString() {
- return "Resend "+toHostPort+" "+resendPacketIds;
- }
-
- public boolean isControlMessage() {
- return false;
- }
-
- public String getToHostPort() {
- return toHostPort;
- }
-
- public void add(long packetId){
- resendPacketIds.add(Long.valueOf(packetId));
- }
-
- public List getResendPacketIds() {
- return resendPacketIds;
- }
-
- public static MessageResend readBinaryMessage(DataInput dataInput) throws IOException {
-
- String hostPort = dataInput.readUTF();
-
- MessageResend msg = new MessageResend(hostPort);
-
- int numberOfPacketIds = dataInput.readInt();
- for (int i = 0; i < numberOfPacketIds; i++) {
- long packetId = dataInput.readLong();
- msg.add(packetId);
- }
-
- return msg;
- }
-
- public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
-
- BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
-
- DataOutputStream os = m.getOs();
- os.writeInt(BinaryMessage.TYPE_MSGRESEND);
- os.writeUTF(toHostPort);
- os.writeInt(resendPacketIds.size());
- for (int i = 0; i < resendPacketIds.size(); i++) {
- Long packetId = resendPacketIds.get(i);
- os.writeLong(packetId.longValue());
- }
- os.flush();
- msgList.add(m);
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.io.DataInput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
+import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
+
+public class MessageResend implements Message {
+
+ private final String toHostPort;
+
+ private final List resendPacketIds;
+
+ public MessageResend(String toHostPort, List resendPacketIds) {
+ this.toHostPort = toHostPort;
+ this.resendPacketIds = resendPacketIds;
+ }
+
+ public MessageResend(String toHostPort) {
+ this(toHostPort, new ArrayList(4));
+ }
+
+ public String toString() {
+ return "Resend "+toHostPort+" "+resendPacketIds;
+ }
+
+ public boolean isControlMessage() {
+ return false;
+ }
+
+ public String getToHostPort() {
+ return toHostPort;
+ }
+
+ public void add(long packetId){
+ resendPacketIds.add(Long.valueOf(packetId));
+ }
+
+ public List getResendPacketIds() {
+ return resendPacketIds;
+ }
+
+ public static MessageResend readBinaryMessage(DataInput dataInput) throws IOException {
+
+ String hostPort = dataInput.readUTF();
+
+ MessageResend msg = new MessageResend(hostPort);
+
+ int numberOfPacketIds = dataInput.readInt();
+ for (int i = 0; i < numberOfPacketIds; i++) {
+ long packetId = dataInput.readLong();
+ msg.add(packetId);
+ }
+
+ return msg;
+ }
+
+ public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
+
+ BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
+
+ DataOutputStream os = m.getOs();
+ os.writeInt(BinaryMessage.TYPE_MSGRESEND);
+ os.writeUTF(toHostPort);
+ os.writeInt(resendPacketIds.size());
+ for (int i = 0; i < resendPacketIds.size(); i++) {
+ Long packetId = resendPacketIds.get(i);
+ os.writeLong(packetId.longValue());
+ }
+ os.flush();
+ msgList.add(m);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java
index 28a5f1bd4..f20405e00 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java
@@ -1,125 +1,106 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class OutgoingPacketsAcked {
-
- private long minimumGotAllPacketId;
-
- private Map recievedByMap = new HashMap();
-
- public int getGroupSize() {
- synchronized (this) {
- return recievedByMap.size();
- }
- }
-
- public long getMinimumGotAllPacketId() {
- synchronized (this) {
- return minimumGotAllPacketId;
- }
- }
-
- public void removeMember(String groupMember){
- synchronized (this) {
- recievedByMap.remove(groupMember);
- resetGotAllMin();
- }
- }
-
- private boolean resetGotAllMin() {
-
- long tempMin;
- if (recievedByMap.isEmpty()){
- //System.out.println(" -- -- -- -- "+recievedByMap.isEmpty());
- tempMin = Long.MAX_VALUE;
- } else {
- tempMin = Long.MAX_VALUE;
- }
-
- for (GroupMemberAck groupMemAck : recievedByMap.values()) {
- long memberMin = groupMemAck.getGotAllPacketId();
- if (memberMin < tempMin){
- //System.out.println(" -- new tmpMin "+memberMin);
- tempMin = memberMin;
- }
- }
-
- if (tempMin != minimumGotAllPacketId) {
- minimumGotAllPacketId = tempMin;
- return true;
- } else {
- return false;
- }
- }
-
- public long receivedAck(String groupMember, MessageAck ack) {
-
- synchronized (this) {
-
- boolean checkMin = false;
-
- GroupMemberAck groupMemberAck = recievedByMap.get(groupMember);
- if (groupMemberAck == null) {
- //System.out.println(" -- new groupMemberAck");
- groupMemberAck = new GroupMemberAck();
- groupMemberAck.setIfBigger(ack.getGotAllPacketId());
- recievedByMap.put(groupMember, groupMemberAck);
- checkMin = true;
- } else {
- checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId;
- //System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId());
- groupMemberAck.setIfBigger(ack.getGotAllPacketId());
- }
-
- boolean minChanged = false;
-
- //System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId);
- if (checkMin || minimumGotAllPacketId == 0){
-
- minChanged = resetGotAllMin();
- //System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId);
- }
-
- return minChanged ? minimumGotAllPacketId : 0;
- }
- }
-
- private static class GroupMemberAck {
-
- private long gotAllPacketId;
-
- private GroupMemberAck() {
- }
-
- private long getGotAllPacketId() {
- return gotAllPacketId;
- }
-
- private void setIfBigger(long newGotAll) {
- if (newGotAll > gotAllPacketId) {
- gotAllPacketId = newGotAll;
- }
- }
- }
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class OutgoingPacketsAcked {
+
+ private long minimumGotAllPacketId;
+
+ private Map recievedByMap = new HashMap();
+
+ public int getGroupSize() {
+ synchronized (this) {
+ return recievedByMap.size();
+ }
+ }
+
+ public long getMinimumGotAllPacketId() {
+ synchronized (this) {
+ return minimumGotAllPacketId;
+ }
+ }
+
+ public void removeMember(String groupMember){
+ synchronized (this) {
+ recievedByMap.remove(groupMember);
+ resetGotAllMin();
+ }
+ }
+
+ private boolean resetGotAllMin() {
+
+ long tempMin;
+ if (recievedByMap.isEmpty()){
+ //System.out.println(" -- -- -- -- "+recievedByMap.isEmpty());
+ tempMin = Long.MAX_VALUE;
+ } else {
+ tempMin = Long.MAX_VALUE;
+ }
+
+ for (GroupMemberAck groupMemAck : recievedByMap.values()) {
+ long memberMin = groupMemAck.getGotAllPacketId();
+ if (memberMin < tempMin){
+ //System.out.println(" -- new tmpMin "+memberMin);
+ tempMin = memberMin;
+ }
+ }
+
+ if (tempMin != minimumGotAllPacketId) {
+ minimumGotAllPacketId = tempMin;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public long receivedAck(String groupMember, MessageAck ack) {
+
+ synchronized (this) {
+
+ boolean checkMin = false;
+
+ GroupMemberAck groupMemberAck = recievedByMap.get(groupMember);
+ if (groupMemberAck == null) {
+ //System.out.println(" -- new groupMemberAck");
+ groupMemberAck = new GroupMemberAck();
+ groupMemberAck.setIfBigger(ack.getGotAllPacketId());
+ recievedByMap.put(groupMember, groupMemberAck);
+ checkMin = true;
+ } else {
+ checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId;
+ //System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId());
+ groupMemberAck.setIfBigger(ack.getGotAllPacketId());
+ }
+
+ boolean minChanged = false;
+
+ //System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId);
+ if (checkMin || minimumGotAllPacketId == 0){
+
+ minChanged = resetGotAllMin();
+ //System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId);
+ }
+
+ return minChanged ? minimumGotAllPacketId : 0;
+ }
+ }
+
+ private static class GroupMemberAck {
+
+ private long gotAllPacketId;
+
+ private GroupMemberAck() {
+ }
+
+ private long getGotAllPacketId() {
+ return gotAllPacketId;
+ }
+
+ private void setIfBigger(long newGotAll) {
+ if (newGotAll > gotAllPacketId) {
+ gotAllPacketId = newGotAll;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java
index a002f1602..be5c103c3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java
@@ -1,85 +1,66 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.mcast;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import com.avaje.ebeaninternal.server.cluster.Packet;
-
-/**
- * Cache of the outgoing packets.
- *
- * These are held until we receive ACKs from the other members of the cluster to
- * say they have received the packets.
- *
- *
- * @author rbygrave
- *
- */
-public class OutgoingPacketsCache {
-
- private final Map packetMap = new TreeMap();
-
- public int size() {
- return packetMap.size();
- }
-
- public Packet getPacket(Long packetId) {
- return packetMap.get(packetId);
- }
-
- public String toString() {
- return packetMap.keySet().toString();
- }
-
- /**
- * Remove the packet when we give up trying to send it out.
- */
- public void remove(Packet packet) {
- packetMap.remove(packet.getPacketId());
- }
-
- public void registerPackets(List packets) {
- for (int i = 0; i < packets.size(); i++) {
- Packet p = packets.get(i);
- packetMap.put(p.getPacketId(), p);
- }
- }
-
- public int trimAll() {
- int size = packetMap.size();
- packetMap.clear();
- return size;
- }
-
- public void trimAcknowledgedMessages(long minAcked) {
- Iterator it = packetMap.keySet().iterator();
- while (it.hasNext()) {
- Long pktId = it.next();
- if (minAcked >= pktId.longValue()) {
- it.remove();
- }
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.mcast;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.avaje.ebeaninternal.server.cluster.Packet;
+
+/**
+ * Cache of the outgoing packets.
+ *
+ * These are held until we receive ACKs from the other members of the cluster to
+ * say they have received the packets.
+ *
+ *
+ * @author rbygrave
+ *
+ */
+public class OutgoingPacketsCache {
+
+ private final Map packetMap = new TreeMap();
+
+ public int size() {
+ return packetMap.size();
+ }
+
+ public Packet getPacket(Long packetId) {
+ return packetMap.get(packetId);
+ }
+
+ public String toString() {
+ return packetMap.keySet().toString();
+ }
+
+ /**
+ * Remove the packet when we give up trying to send it out.
+ */
+ public void remove(Packet packet) {
+ packetMap.remove(packet.getPacketId());
+ }
+
+ public void registerPackets(List packets) {
+ for (int i = 0; i < packets.size(); i++) {
+ Packet p = packets.get(i);
+ packetMap.put(p.getPacketId(), p);
+ }
+ }
+
+ public int trimAll() {
+ int size = packetMap.size();
+ packetMap.clear();
+ return size;
+ }
+
+ public void trimAcknowledgedMessages(long minAcked) {
+ Iterator it = packetMap.keySet().iterator();
+ while (it.hasNext()) {
+ Long pktId = it.next();
+ if (minAcked >= pktId.longValue()) {
+ it.remove();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java
index d4a286a49..6b9f4ca97 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java
@@ -1,76 +1,59 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * This parses and dispatches a request to the appropriate handler.
- *
- * Looks up the appropriate RequestHandler
- * and then gets it to process the Client request.
- *
- * Note that this is a Runnable because it is assigned to the ThreadPool.
- */
-class RequestProcessor implements Runnable {
-
- private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName());
-
- private final Socket clientSocket;
-
- private final SocketClusterBroadcast owner;
-
- /**
- * Create including the Listener (used to lookup the Request Handler) and
- * the socket itself.
- */
- public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
- this.clientSocket = clientSocket;
- this.owner = owner;
- }
-
- /**
- * This will parse out the command. Lookup the appropriate Handler and
- * pass the information to the handler for processing.
- * Dev Note: the command parsing is processed here so that it is preformed
- * by the assigned thread rather than the listeners thread.
- */
- public void run() {
- try {
- SocketConnection sc = new SocketConnection(clientSocket);
-
- while(true){
- if (owner.process(sc)) {
- // got the offline message or timeout
- break;
- }
- }
- sc.disconnect();
-
- } catch (IOException e) {
- logger.log(Level.SEVERE, null, e);
- } catch (ClassNotFoundException e) {
- logger.log(Level.SEVERE, null, e);
- }
- }
-
-
-};
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This parses and dispatches a request to the appropriate handler.
+ *
+ * Looks up the appropriate RequestHandler
+ * and then gets it to process the Client request.
+ *
+ * Note that this is a Runnable because it is assigned to the ThreadPool.
+ */
+class RequestProcessor implements Runnable {
+
+ private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName());
+
+ private final Socket clientSocket;
+
+ private final SocketClusterBroadcast owner;
+
+ /**
+ * Create including the Listener (used to lookup the Request Handler) and
+ * the socket itself.
+ */
+ public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
+ this.clientSocket = clientSocket;
+ this.owner = owner;
+ }
+
+ /**
+ * This will parse out the command. Lookup the appropriate Handler and
+ * pass the information to the handler for processing.
+ * Dev Note: the command parsing is processed here so that it is preformed
+ * by the assigned thread rather than the listeners thread.
+ */
+ public void run() {
+ try {
+ SocketConnection sc = new SocketConnection(clientSocket);
+
+ while(true){
+ if (owner.process(sc)) {
+ // got the offline message or timeout
+ break;
+ }
+ }
+ sc.disconnect();
+
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, null, e);
+ } catch (ClassNotFoundException e) {
+ logger.log(Level.SEVERE, null, e);
+ }
+ }
+
+
+};
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java
index f79e10590..7ab8f5764 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java
@@ -1,151 +1,134 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-
-/**
- * The client side of the socket clustering.
- */
-class SocketClient {
-
- private static final Logger logger = Logger.getLogger(SocketClient.class.getName());
-
- private final InetSocketAddress address;
-
- private final String hostPort;
-
- private boolean online;
-
- private Socket socket;
- private OutputStream os;
- private ObjectOutputStream oos;
-
- /**
- * Construct with an IP address and port.
- */
- public SocketClient(InetSocketAddress address) {
- this.address = address;
- this.hostPort = address.getHostName()+":"+address.getPort();
- }
-
- public String getHostPort() {
- return hostPort;
- }
-
- public int getPort() {
- return address.getPort();
- }
-
- public boolean isOnline() {
- return online;
- }
-
- public void setOnline(boolean online) throws IOException {
- if (online){
- setOnline();
- } else {
- disconnect();
- }
- }
-
-
- /**
- * Set whether the client is thought to be online.
- */
- private void setOnline() throws IOException {
- connect();
- this.online = true;
- }
-
- public void reconnect() throws IOException {
- disconnect();
- connect();
- }
-
- private void connect() throws IOException {
- if (socket != null){
- throw new IllegalStateException("Already got a socket connection?");
- }
- Socket s = new Socket();
- s.setKeepAlive(true);
- s.connect(address);
-
- this.socket = s;
- this.os = socket.getOutputStream();
- }
-
- public void disconnect() {
- this.online = false;
- if (socket != null){
-
- try {
- socket.close();
- } catch (IOException e) {
- String msg = "Error disconnecting from Cluster member "+hostPort;
- logger.log(Level.INFO, msg, e);
- }
-
- os = null;
- oos = null;
- socket = null;
- }
- }
-
- public boolean register(SocketClusterMessage registerMsg) {
-
- try {
- setOnline();
- send(registerMsg);
- return true;
- } catch (IOException e) {
- disconnect();
- return false;
- }
- }
-
- public boolean send(SocketClusterMessage msg) throws IOException {
-
- if (online){
- writeObject(msg);
- return true;
-
- } else {
- return false;
- }
-
- }
-
- private void writeObject(Object object) throws IOException {
- if (oos == null){
- this.oos = new ObjectOutputStream(os);
- }
- oos.writeObject(object);
- oos.flush();
- }
-
-
-
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * The client side of the socket clustering.
+ */
+class SocketClient {
+
+ private static final Logger logger = Logger.getLogger(SocketClient.class.getName());
+
+ private final InetSocketAddress address;
+
+ private final String hostPort;
+
+ private boolean online;
+
+ private Socket socket;
+ private OutputStream os;
+ private ObjectOutputStream oos;
+
+ /**
+ * Construct with an IP address and port.
+ */
+ public SocketClient(InetSocketAddress address) {
+ this.address = address;
+ this.hostPort = address.getHostName()+":"+address.getPort();
+ }
+
+ public String getHostPort() {
+ return hostPort;
+ }
+
+ public int getPort() {
+ return address.getPort();
+ }
+
+ public boolean isOnline() {
+ return online;
+ }
+
+ public void setOnline(boolean online) throws IOException {
+ if (online){
+ setOnline();
+ } else {
+ disconnect();
+ }
+ }
+
+
+ /**
+ * Set whether the client is thought to be online.
+ */
+ private void setOnline() throws IOException {
+ connect();
+ this.online = true;
+ }
+
+ public void reconnect() throws IOException {
+ disconnect();
+ connect();
+ }
+
+ private void connect() throws IOException {
+ if (socket != null){
+ throw new IllegalStateException("Already got a socket connection?");
+ }
+ Socket s = new Socket();
+ s.setKeepAlive(true);
+ s.connect(address);
+
+ this.socket = s;
+ this.os = socket.getOutputStream();
+ }
+
+ public void disconnect() {
+ this.online = false;
+ if (socket != null){
+
+ try {
+ socket.close();
+ } catch (IOException e) {
+ String msg = "Error disconnecting from Cluster member "+hostPort;
+ logger.log(Level.INFO, msg, e);
+ }
+
+ os = null;
+ oos = null;
+ socket = null;
+ }
+ }
+
+ public boolean register(SocketClusterMessage registerMsg) {
+
+ try {
+ setOnline();
+ send(registerMsg);
+ return true;
+ } catch (IOException e) {
+ disconnect();
+ return false;
+ }
+ }
+
+ public boolean send(SocketClusterMessage msg) throws IOException {
+
+ if (online){
+ writeObject(msg);
+ return true;
+
+ } else {
+ return false;
+ }
+
+ }
+
+ private void writeObject(Object object) throws IOException {
+ if (oos == null){
+ this.oos = new ObjectOutputStream(os);
+ }
+ oos.writeObject(object);
+ oos.flush();
+ }
+
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java
index b2f403636..971d217e8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java
@@ -1,265 +1,248 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.InetSocketAddress;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.persistence.PersistenceException;
-
-import com.avaje.ebean.config.GlobalProperties;
-import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
-import com.avaje.ebeaninternal.server.cluster.ClusterManager;
-import com.avaje.ebeaninternal.server.cluster.DataHolder;
-import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
-import com.avaje.ebeaninternal.server.lib.util.StringHelper;
-import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
-
-/**
- * Broadcast messages across the cluster using sockets.
- */
-public class SocketClusterBroadcast implements ClusterBroadcast {
-
- private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName());
-
- private final SocketClient local;
-
- private final HashMap clientMap;
-
- private final SocketClusterListener listener;
-
- private SocketClient[] members;
-
- private ClusterManager clusterManager;
-
- private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
-
- private final AtomicInteger txnOutgoing = new AtomicInteger();
- private final AtomicInteger txnIncoming = new AtomicInteger();
-
-
- public SocketClusterBroadcast( ){
-
- String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
- String members = GlobalProperties.get("ebean.cluster.members", null);
-
- logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
-
- this.local = new SocketClient(parseFullName(localHostPort));
- this.clientMap = new HashMap();
-
- String[] memArray = StringHelper.delimitedToArray(members, ",", false);
- for (int i = 0; i < memArray.length; i++) {
- InetSocketAddress member = parseFullName(memArray[i]);
- SocketClient client = new SocketClient(member);
- if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
- // don't add the local one ...
- clientMap.put(client.getHostPort(), client);
- }
- }
-
- this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
- this.listener = new SocketClusterListener(this, local.getPort());
- }
-
- /**
- * Return the current status of this instance.
- */
- public SocketClusterStatus getStatus() {
-
- // count of online members
- int currentGroupSize = 0;
- for (int i = 0; i < members.length; i++) {
- if (members[i].isOnline()) {
- ++currentGroupSize;
- }
- }
- int txnIn = txnIncoming.get();
- int txnOut = txnOutgoing.get();
-
- return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
- }
-
- public void startup(ClusterManager clusterManager) {
-
- this.clusterManager = clusterManager;
- try {
- listener.startListening();
- register();
-
- } catch (IOException e) {
- throw new PersistenceException(e);
- }
- }
-
- public void shutdown() {
- deregister();
- listener.shutdown();
- }
-
- /**
- * Register with all the other members of the Cluster.
- */
- private void register() {
-
- SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
-
- for (int i = 0; i < members.length; i++) {
- boolean online = members[i].register(h);
-
- String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
- logger.info(msg);
- }
- }
-
- protected void setMemberOnline(String fullName, boolean online) throws IOException {
- synchronized (clientMap) {
- String msg = "Cluster Member ["+fullName+"] online["+online+"]";
- logger.info(msg);
- SocketClient member = clientMap.get(fullName);
- member.setOnline(online);
- }
- }
-
- private void send(SocketClient client, SocketClusterMessage msg) {
-
- try {
- // alternative would be to connect/disconnect here
- // but prefer to use keepalive
- client.send(msg);
-
- } catch (Exception ex){
- logger.log(Level.SEVERE, "Error sending message", ex);
- try {
- client.reconnect();
- } catch (IOException e) {
- logger.log(Level.SEVERE, "Error trying to reconnect", ex);
- }
- }
- }
-
- /**
- * Send the payload to all the members of the cluster.
- */
- public void broadcast(RemoteTransactionEvent remoteTransEvent) {
- try {
-
- txnOutgoing.incrementAndGet();
- DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
- SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
- broadcast(msg);
- } catch (Exception e){
- String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
- logger.log(Level.SEVERE, msg, e);
- }
- }
-
- protected void broadcast(SocketClusterMessage msg) {
-
- for (int i = 0; i < members.length; i++) {
- send(members[i], msg);
- }
- }
-
- /**
- * Leave the cluster.
- */
- private void deregister() {
-
- SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
- broadcast(h);
-
- for (int i = 0; i < members.length; i++) {
- members[i].disconnect();
- }
- }
-
- /**
- * Process a Cluster message.
- */
- protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
-
- try {
- SocketClusterMessage h = (SocketClusterMessage)request.readObject();
-
- if (h.isRegisterEvent()){
- setMemberOnline(h.getRegisterHost(), h.isRegister());
-
- } else {
- txnIncoming.incrementAndGet();
- DataHolder dataHolder = h.getDataHolder();
- RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
- transEvent.run();
- }
-
- if (h.isRegisterEvent() && !h.isRegister()){
- // instance shutting down
- return true;
- } else {
- return false;
- }
- } catch (InterruptedIOException e) {
- String msg = "Timeout waiting for message";
- logger.log(Level.INFO, msg, e);
- try {
- request.disconnect();
- } catch (IOException ex){
- logger.log(Level.INFO, "Error disconnecting after timeout", ex);
- }
- return true;
- }
- }
-
-
- /**
- * Parse a host:port into a InetSocketAddress.
- */
- private InetSocketAddress parseFullName(String hostAndPort) {
-
- try {
- hostAndPort = hostAndPort.trim();
- int colonPos = hostAndPort.indexOf(":");
- if (colonPos == -1) {
- String msg = "No colon \":\" in "+hostAndPort;
- throw new IllegalArgumentException(msg);
- }
- String host = hostAndPort.substring(0, colonPos);
- String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
- int port = Integer.parseInt(sPort);
-
- return new InetSocketAddress(host, port);
-
- } catch (Exception ex){
- throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
- }
- }
-
- class TxnSerialiseHelper extends SerialiseTransactionHelper {
-
- @Override
- public SpiEbeanServer getEbeanServer(String serverName) {
- return (SpiEbeanServer)clusterManager.getServer(serverName);
- }
- }
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.persistence.PersistenceException;
+
+import com.avaje.ebean.config.GlobalProperties;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
+import com.avaje.ebeaninternal.server.cluster.ClusterManager;
+import com.avaje.ebeaninternal.server.cluster.DataHolder;
+import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
+import com.avaje.ebeaninternal.server.lib.util.StringHelper;
+import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
+
+/**
+ * Broadcast messages across the cluster using sockets.
+ */
+public class SocketClusterBroadcast implements ClusterBroadcast {
+
+ private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName());
+
+ private final SocketClient local;
+
+ private final HashMap clientMap;
+
+ private final SocketClusterListener listener;
+
+ private SocketClient[] members;
+
+ private ClusterManager clusterManager;
+
+ private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
+
+ private final AtomicInteger txnOutgoing = new AtomicInteger();
+ private final AtomicInteger txnIncoming = new AtomicInteger();
+
+
+ public SocketClusterBroadcast( ){
+
+ String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
+ String members = GlobalProperties.get("ebean.cluster.members", null);
+
+ logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
+
+ this.local = new SocketClient(parseFullName(localHostPort));
+ this.clientMap = new HashMap();
+
+ String[] memArray = StringHelper.delimitedToArray(members, ",", false);
+ for (int i = 0; i < memArray.length; i++) {
+ InetSocketAddress member = parseFullName(memArray[i]);
+ SocketClient client = new SocketClient(member);
+ if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
+ // don't add the local one ...
+ clientMap.put(client.getHostPort(), client);
+ }
+ }
+
+ this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
+ this.listener = new SocketClusterListener(this, local.getPort());
+ }
+
+ /**
+ * Return the current status of this instance.
+ */
+ public SocketClusterStatus getStatus() {
+
+ // count of online members
+ int currentGroupSize = 0;
+ for (int i = 0; i < members.length; i++) {
+ if (members[i].isOnline()) {
+ ++currentGroupSize;
+ }
+ }
+ int txnIn = txnIncoming.get();
+ int txnOut = txnOutgoing.get();
+
+ return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
+ }
+
+ public void startup(ClusterManager clusterManager) {
+
+ this.clusterManager = clusterManager;
+ try {
+ listener.startListening();
+ register();
+
+ } catch (IOException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ public void shutdown() {
+ deregister();
+ listener.shutdown();
+ }
+
+ /**
+ * Register with all the other members of the Cluster.
+ */
+ private void register() {
+
+ SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
+
+ for (int i = 0; i < members.length; i++) {
+ boolean online = members[i].register(h);
+
+ String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
+ logger.info(msg);
+ }
+ }
+
+ protected void setMemberOnline(String fullName, boolean online) throws IOException {
+ synchronized (clientMap) {
+ String msg = "Cluster Member ["+fullName+"] online["+online+"]";
+ logger.info(msg);
+ SocketClient member = clientMap.get(fullName);
+ member.setOnline(online);
+ }
+ }
+
+ private void send(SocketClient client, SocketClusterMessage msg) {
+
+ try {
+ // alternative would be to connect/disconnect here
+ // but prefer to use keepalive
+ client.send(msg);
+
+ } catch (Exception ex){
+ logger.log(Level.SEVERE, "Error sending message", ex);
+ try {
+ client.reconnect();
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Error trying to reconnect", ex);
+ }
+ }
+ }
+
+ /**
+ * Send the payload to all the members of the cluster.
+ */
+ public void broadcast(RemoteTransactionEvent remoteTransEvent) {
+ try {
+
+ txnOutgoing.incrementAndGet();
+ DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
+ SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
+ broadcast(msg);
+ } catch (Exception e){
+ String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
+ logger.log(Level.SEVERE, msg, e);
+ }
+ }
+
+ protected void broadcast(SocketClusterMessage msg) {
+
+ for (int i = 0; i < members.length; i++) {
+ send(members[i], msg);
+ }
+ }
+
+ /**
+ * Leave the cluster.
+ */
+ private void deregister() {
+
+ SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
+ broadcast(h);
+
+ for (int i = 0; i < members.length; i++) {
+ members[i].disconnect();
+ }
+ }
+
+ /**
+ * Process a Cluster message.
+ */
+ protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
+
+ try {
+ SocketClusterMessage h = (SocketClusterMessage)request.readObject();
+
+ if (h.isRegisterEvent()){
+ setMemberOnline(h.getRegisterHost(), h.isRegister());
+
+ } else {
+ txnIncoming.incrementAndGet();
+ DataHolder dataHolder = h.getDataHolder();
+ RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
+ transEvent.run();
+ }
+
+ if (h.isRegisterEvent() && !h.isRegister()){
+ // instance shutting down
+ return true;
+ } else {
+ return false;
+ }
+ } catch (InterruptedIOException e) {
+ String msg = "Timeout waiting for message";
+ logger.log(Level.INFO, msg, e);
+ try {
+ request.disconnect();
+ } catch (IOException ex){
+ logger.log(Level.INFO, "Error disconnecting after timeout", ex);
+ }
+ return true;
+ }
+ }
+
+
+ /**
+ * Parse a host:port into a InetSocketAddress.
+ */
+ private InetSocketAddress parseFullName(String hostAndPort) {
+
+ try {
+ hostAndPort = hostAndPort.trim();
+ int colonPos = hostAndPort.indexOf(":");
+ if (colonPos == -1) {
+ String msg = "No colon \":\" in "+hostAndPort;
+ throw new IllegalArgumentException(msg);
+ }
+ String host = hostAndPort.substring(0, colonPos);
+ String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
+ int port = Integer.parseInt(sPort);
+
+ return new InetSocketAddress(host, port);
+
+ } catch (Exception ex){
+ throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
+ }
+ }
+
+ class TxnSerialiseHelper extends SerialiseTransactionHelper {
+
+ @Override
+ public SpiEbeanServer getEbeanServer(String serverName) {
+ return (SpiEbeanServer)clusterManager.getServer(serverName);
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java
index ba3fc6529..c9c049c80 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java
@@ -1,181 +1,164 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
-import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager;
-
-/**
- * Serverside multithreaded socket listener. Accepts connections and dispatches
- * them to an appropriate handler.
- *
- * This is designed as a single port listener, where part of the connection
- * protocol determines which service the client is requesting (rather than a
- * port per service).
- *
- *
- * It has its own daemon background thread that handles the accept() loop on the
- * ServerSocket.
- *
- */
-class SocketClusterListener implements Runnable {
-
- private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName());
-
- /**
- * The port the SocketListener uses.
- */
- private final int port;
-
- /**
- * The length of the socket accept timeout.
- */
- private final int listenTimeout = 60000;
-
- /**
- * The server socket used to listen for requests.
- */
- private final ServerSocket serverListenSocket;
-
- /**
- * The listening thread.
- */
- private final Thread listenerThread;
-
- /**
- * The pool of threads that actually do the parsing execution of requests.
- */
- private final ThreadPool threadPool;
-
- private final SocketClusterBroadcast owner;
-
- /**
- * shutting down flag.
- */
- boolean doingShutdown;
-
- /**
- * Whether the listening thread is busy assigning a request to a thread.
- */
- boolean isActive;
-
- /**
- * Construct with a given thread pool name.
- */
- public SocketClusterListener(SocketClusterBroadcast owner, int port) {
- this.owner = owner;
- this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember");
- this.port = port;
-
- try {
- this.serverListenSocket = new ServerSocket(port);
- this.serverListenSocket.setSoTimeout(listenTimeout);
- this.listenerThread = new Thread(this, "EbeanClusterListener");
-
- } catch (IOException e){
- String msg = "Error starting cluster socket listener on port "+port;
- throw new RuntimeException(msg,e);
- }
- }
-
- /**
- * Returns the port the listener is using.
- */
- public int getPort() {
- return port;
- }
-
- /**
- * Start listening for requests.
- */
- public void startListening() throws IOException {
- this.listenerThread.setDaemon(true);
- this.listenerThread.start();
- }
-
- /**
- * Shutdown this listener.
- */
- public void shutdown() {
- doingShutdown = true;
- try {
- if (isActive) {
- synchronized (listenerThread) {
- try {
- listenerThread.wait(1000);
- } catch (InterruptedException e) {
- // OK to ignore as expected to Interrupt for shutdown.
- ;
- }
- }
- }
- listenerThread.interrupt();
- serverListenSocket.close();
- } catch (IOException e) {
- logger.log(Level.SEVERE, null, e);
- }
- }
-
- /**
- * This is a runnable and so this must be public. Don't call this externally
- * but rather call the startListening() method.
- */
- public void run() {
- // run in loop until doingShutdown is true...
- while (!doingShutdown) {
- try {
- synchronized (listenerThread) {
- Socket clientSocket = serverListenSocket.accept();
-
- isActive = true;
-
- Runnable request = new RequestProcessor(owner, clientSocket);
- threadPool.assign(request, true);
-
- isActive = false;
- }
- } catch (SocketException e) {
- if (doingShutdown) {
- String msg = "doingShutdown and accept threw:"+ e.getMessage();
- logger.info(msg);
-
- } else {
- logger.log(Level.SEVERE, null, e);
- }
-
- } catch (InterruptedIOException e) {
- // this will happen when the server is very quiet.
- // that is, no requests
- logger.fine("Possibly expected due to accept timeout?" + e.getMessage());
-
- } catch (IOException e) {
- // log it and continue in the loop...
- logger.log(Level.SEVERE, null, e);
- }
- }
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
+import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager;
+
+/**
+ * Serverside multithreaded socket listener. Accepts connections and dispatches
+ * them to an appropriate handler.
+ *
+ * This is designed as a single port listener, where part of the connection
+ * protocol determines which service the client is requesting (rather than a
+ * port per service).
+ *
+ *
+ * It has its own daemon background thread that handles the accept() loop on the
+ * ServerSocket.
+ *
+ */
+class SocketClusterListener implements Runnable {
+
+ private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName());
+
+ /**
+ * The port the SocketListener uses.
+ */
+ private final int port;
+
+ /**
+ * The length of the socket accept timeout.
+ */
+ private final int listenTimeout = 60000;
+
+ /**
+ * The server socket used to listen for requests.
+ */
+ private final ServerSocket serverListenSocket;
+
+ /**
+ * The listening thread.
+ */
+ private final Thread listenerThread;
+
+ /**
+ * The pool of threads that actually do the parsing execution of requests.
+ */
+ private final ThreadPool threadPool;
+
+ private final SocketClusterBroadcast owner;
+
+ /**
+ * shutting down flag.
+ */
+ boolean doingShutdown;
+
+ /**
+ * Whether the listening thread is busy assigning a request to a thread.
+ */
+ boolean isActive;
+
+ /**
+ * Construct with a given thread pool name.
+ */
+ public SocketClusterListener(SocketClusterBroadcast owner, int port) {
+ this.owner = owner;
+ this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember");
+ this.port = port;
+
+ try {
+ this.serverListenSocket = new ServerSocket(port);
+ this.serverListenSocket.setSoTimeout(listenTimeout);
+ this.listenerThread = new Thread(this, "EbeanClusterListener");
+
+ } catch (IOException e){
+ String msg = "Error starting cluster socket listener on port "+port;
+ throw new RuntimeException(msg,e);
+ }
+ }
+
+ /**
+ * Returns the port the listener is using.
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Start listening for requests.
+ */
+ public void startListening() throws IOException {
+ this.listenerThread.setDaemon(true);
+ this.listenerThread.start();
+ }
+
+ /**
+ * Shutdown this listener.
+ */
+ public void shutdown() {
+ doingShutdown = true;
+ try {
+ if (isActive) {
+ synchronized (listenerThread) {
+ try {
+ listenerThread.wait(1000);
+ } catch (InterruptedException e) {
+ // OK to ignore as expected to Interrupt for shutdown.
+ ;
+ }
+ }
+ }
+ listenerThread.interrupt();
+ serverListenSocket.close();
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, null, e);
+ }
+ }
+
+ /**
+ * This is a runnable and so this must be public. Don't call this externally
+ * but rather call the startListening() method.
+ */
+ public void run() {
+ // run in loop until doingShutdown is true...
+ while (!doingShutdown) {
+ try {
+ synchronized (listenerThread) {
+ Socket clientSocket = serverListenSocket.accept();
+
+ isActive = true;
+
+ Runnable request = new RequestProcessor(owner, clientSocket);
+ threadPool.assign(request, true);
+
+ isActive = false;
+ }
+ } catch (SocketException e) {
+ if (doingShutdown) {
+ String msg = "doingShutdown and accept threw:"+ e.getMessage();
+ logger.info(msg);
+
+ } else {
+ logger.log(Level.SEVERE, null, e);
+ }
+
+ } catch (InterruptedIOException e) {
+ // this will happen when the server is very quiet.
+ // that is, no requests
+ logger.fine("Possibly expected due to accept timeout?" + e.getMessage());
+
+ } catch (IOException e) {
+ // log it and continue in the loop...
+ logger.log(Level.SEVERE, null, e);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java
index 47015de8b..71bc8b572 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java
@@ -1,95 +1,78 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.Serializable;
-
-import com.avaje.ebeaninternal.server.cluster.DataHolder;
-import com.avaje.ebeaninternal.server.cluster.Packet;
-
-/**
- * The messages broadcast around the cluster.
- */
-public class SocketClusterMessage implements Serializable {
-
- private static final long serialVersionUID = 2993350408394934473L;
-
- private final String registerHost;
-
- private final boolean register;
-
- private final DataHolder dataHolder;
-
- public static SocketClusterMessage register(String registerHost, boolean register){
- return new SocketClusterMessage(registerHost, register);
- }
-
- public static SocketClusterMessage transEvent(DataHolder transEvent){
- return new SocketClusterMessage(transEvent);
- }
-
- public static SocketClusterMessage packet(Packet packet){
- DataHolder d = new DataHolder(packet.getBytes());
- return new SocketClusterMessage(d);
- }
-
- /**
- * Used to construct a Child AttributeMap.
- */
- private SocketClusterMessage(String registerHost, boolean register) {
- this.registerHost = registerHost;
- this.register = register;
- this.dataHolder = null;
- }
-
- private SocketClusterMessage(DataHolder dataHolder) {
- this.dataHolder = dataHolder;
- this.registerHost = null;
- this.register = false;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- if (registerHost != null){
- sb.append("register ");
- sb.append(register);
- sb.append(" ");
- sb.append(registerHost);
- } else {
- sb.append("transEvent ");
- }
- return sb.toString();
- }
-
- public boolean isRegisterEvent() {
- return registerHost != null;
- }
-
- public String getRegisterHost() {
- return registerHost;
- }
-
- public boolean isRegister() {
- return register;
- }
-
- public DataHolder getDataHolder() {
- return dataHolder;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.Serializable;
+
+import com.avaje.ebeaninternal.server.cluster.DataHolder;
+import com.avaje.ebeaninternal.server.cluster.Packet;
+
+/**
+ * The messages broadcast around the cluster.
+ */
+public class SocketClusterMessage implements Serializable {
+
+ private static final long serialVersionUID = 2993350408394934473L;
+
+ private final String registerHost;
+
+ private final boolean register;
+
+ private final DataHolder dataHolder;
+
+ public static SocketClusterMessage register(String registerHost, boolean register){
+ return new SocketClusterMessage(registerHost, register);
+ }
+
+ public static SocketClusterMessage transEvent(DataHolder transEvent){
+ return new SocketClusterMessage(transEvent);
+ }
+
+ public static SocketClusterMessage packet(Packet packet){
+ DataHolder d = new DataHolder(packet.getBytes());
+ return new SocketClusterMessage(d);
+ }
+
+ /**
+ * Used to construct a Child AttributeMap.
+ */
+ private SocketClusterMessage(String registerHost, boolean register) {
+ this.registerHost = registerHost;
+ this.register = register;
+ this.dataHolder = null;
+ }
+
+ private SocketClusterMessage(DataHolder dataHolder) {
+ this.dataHolder = dataHolder;
+ this.registerHost = null;
+ this.register = false;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (registerHost != null){
+ sb.append("register ");
+ sb.append(register);
+ sb.append(" ");
+ sb.append(registerHost);
+ } else {
+ sb.append("transEvent ");
+ }
+ return sb.toString();
+ }
+
+ public boolean isRegisterEvent() {
+ return registerHost != null;
+ }
+
+ public String getRegisterHost() {
+ return registerHost;
+ }
+
+ public boolean isRegister() {
+ return register;
+ }
+
+ public DataHolder getDataHolder() {
+ return dataHolder;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java
index ad4c46060..fa06064db 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java
@@ -1,60 +1,41 @@
-/**
- * Copyright (C) 2009 Authors
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-/**
- * The current state of this cluster member.
- *
- * @author rbygrave
- */
-public class SocketClusterStatus {
-
- private final int currentGroupSize;
- private final int txnIncoming;
- private final int txtOutgoing;
-
- public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) {
- this.currentGroupSize = currentGroupSize;
- this.txnIncoming = txnIncoming;
- this.txtOutgoing = txnOutgoing;
- }
-
- /**
- * Return the number of members of the cluster currently online.
- */
- public int getCurrentGroupSize() {
- return currentGroupSize;
- }
-
- /**
- * Return the number of Remote transactions received.
- */
- public int getTxnIncoming() {
- return txnIncoming;
- }
-
- /**
- * Return the number of transactions sent to the cluster.
- */
- public int getTxtOutgoing() {
- return txtOutgoing;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+/**
+ * The current state of this cluster member.
+ *
+ * @author rbygrave
+ */
+public class SocketClusterStatus {
+
+ private final int currentGroupSize;
+ private final int txnIncoming;
+ private final int txtOutgoing;
+
+ public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) {
+ this.currentGroupSize = currentGroupSize;
+ this.txnIncoming = txnIncoming;
+ this.txtOutgoing = txnOutgoing;
+ }
+
+ /**
+ * Return the number of members of the cluster currently online.
+ */
+ public int getCurrentGroupSize() {
+ return currentGroupSize;
+ }
+
+ /**
+ * Return the number of Remote transactions received.
+ */
+ public int getTxnIncoming() {
+ return txnIncoming;
+ }
+
+ /**
+ * Return the number of transactions sent to the cluster.
+ */
+ public int getTxtOutgoing() {
+ return txtOutgoing;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java
index 7f25412aa..aeef8e95b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java
@@ -1,146 +1,129 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.cluster.socket;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-/**
- * The client side of a TCP Sockect connection.
- */
-class SocketConnection {
-
- /**
- * The object underlying objectOutputStream.
- */
- ObjectOutputStream oos;
-
- /**
- * The underlying ObjectInputStream.
- */
- ObjectInputStream ois;
-
- /**
- * The underlying inputStream.
- */
- InputStream is;
-
- /**
- * The underlying outputStream.
- */
- OutputStream os;
-
- /**
- * The underlying socket.
- */
- Socket socket;
-
- /**
- * Create for a given Socket.
- */
- public SocketConnection(Socket socket) throws IOException {
- this.is = socket.getInputStream();
- this.os = socket.getOutputStream();
- this.socket = socket;
- }
-
- /**
- * Disconnect from the server.
- */
- public void disconnect() throws IOException {
- os.flush();
- socket.close();
- }
-
- /**
- * Flush the outputStream.
- */
- public void flush() throws IOException {
- os.flush();
- }
-
- /**
- * Read an object from the object input stream.
- */
- public Object readObject() throws IOException, ClassNotFoundException {
- return getObjectInputStream().readObject();
- }
-
- /**
- * Write an object to the object output stream.
- */
- public ObjectOutputStream writeObject(Object object) throws IOException {
- ObjectOutputStream oos = getObjectOutputStream();
- oos.writeObject(object);
- return oos;
- }
-
- /**
- * Get the object output stream.
- */
- public ObjectOutputStream getObjectOutputStream() throws IOException {
- if (oos == null){
- oos = new ObjectOutputStream(os);
- }
- return oos;
- }
-
- /**
- * Get the object input stream.
- */
- public ObjectInputStream getObjectInputStream() throws IOException {
- if (ois == null){
- ois = new ObjectInputStream(is);
- }
- return ois;
- }
-
-
- /**
- * Set the ObjectInputStream to use.
- */
- public void setObjectInputStream(ObjectInputStream ois) {
- this.ois = ois;
- }
-
- /**
- * Set the ObjectOutputStream to use.
- */
- public void setObjectOutputStream(ObjectOutputStream oos) {
- this.oos = oos;
- }
-
- /**
- * Return the underlying input stream.
- */
- public InputStream getInputStream() throws IOException {
- return is;
- }
-
- /**
- * Return the underlying output stream.
- */
- public OutputStream getOutputStream() throws IOException {
- return os;
- }
-
-}
+package com.avaje.ebeaninternal.server.cluster.socket;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * The client side of a TCP Sockect connection.
+ */
+class SocketConnection {
+
+ /**
+ * The object underlying objectOutputStream.
+ */
+ ObjectOutputStream oos;
+
+ /**
+ * The underlying ObjectInputStream.
+ */
+ ObjectInputStream ois;
+
+ /**
+ * The underlying inputStream.
+ */
+ InputStream is;
+
+ /**
+ * The underlying outputStream.
+ */
+ OutputStream os;
+
+ /**
+ * The underlying socket.
+ */
+ Socket socket;
+
+ /**
+ * Create for a given Socket.
+ */
+ public SocketConnection(Socket socket) throws IOException {
+ this.is = socket.getInputStream();
+ this.os = socket.getOutputStream();
+ this.socket = socket;
+ }
+
+ /**
+ * Disconnect from the server.
+ */
+ public void disconnect() throws IOException {
+ os.flush();
+ socket.close();
+ }
+
+ /**
+ * Flush the outputStream.
+ */
+ public void flush() throws IOException {
+ os.flush();
+ }
+
+ /**
+ * Read an object from the object input stream.
+ */
+ public Object readObject() throws IOException, ClassNotFoundException {
+ return getObjectInputStream().readObject();
+ }
+
+ /**
+ * Write an object to the object output stream.
+ */
+ public ObjectOutputStream writeObject(Object object) throws IOException {
+ ObjectOutputStream oos = getObjectOutputStream();
+ oos.writeObject(object);
+ return oos;
+ }
+
+ /**
+ * Get the object output stream.
+ */
+ public ObjectOutputStream getObjectOutputStream() throws IOException {
+ if (oos == null){
+ oos = new ObjectOutputStream(os);
+ }
+ return oos;
+ }
+
+ /**
+ * Get the object input stream.
+ */
+ public ObjectInputStream getObjectInputStream() throws IOException {
+ if (ois == null){
+ ois = new ObjectInputStream(is);
+ }
+ return ois;
+ }
+
+
+ /**
+ * Set the ObjectInputStream to use.
+ */
+ public void setObjectInputStream(ObjectInputStream ois) {
+ this.ois = ois;
+ }
+
+ /**
+ * Set the ObjectOutputStream to use.
+ */
+ public void setObjectOutputStream(ObjectOutputStream oos) {
+ this.oos = oos;
+ }
+
+ /**
+ * Return the underlying input stream.
+ */
+ public InputStream getInputStream() throws IOException {
+ return is;
+ }
+
+ /**
+ * Return the underlying output stream.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return os;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java b/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java
index 7680ede4d..761291f26 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java
@@ -1,493 +1,474 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.core;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.Calendar;
-import java.util.UUID;
-
-
-/**
- * Default implementation of TypeConverter.
- *
- * Converts objects to the required type if required.
- *
- */
-public final class BasicTypeConverter implements Serializable {
-
- private static final long serialVersionUID = 7691463236204070311L;
-
- /**
- * Type code for java.util.Calendar.
- */
- public static final int UTIL_CALENDAR = -999998986;
-
- /**
- * Type code for java.util.Date.
- */
- public static final int UTIL_DATE = -999998988;
-
- /**
- * Type code for java.math.BigInteger.
- */
- public static final int MATH_BIGINTEGER = -999998987;
-
- /**
- * Type code for an Enum type.
- */
- public static final int ENUM = -999998989;
-
- private BasicTypeConverter() {
- }
-
- /**
- * Convert the Object to the required data type.
- *
- * @param value
- * the Object value
- * @param toDataType
- * the dataType as per java.sql.Types.
- */
- public static Object convert(Object value, int toDataType) {
-
- try {
- switch (toDataType) {
- case UTIL_DATE: {
- return toUtilDate(value);
- }
- case UTIL_CALENDAR: {
- return toCalendar(value);
- }
- case Types.BIGINT: {
- return toLong(value);
- }
- case Types.INTEGER: {
- return toInteger(value);
- }
- case Types.BIT: {
- return toBoolean(value);
- }
- case Types.TINYINT: {
- return toByte(value);
- }
- case Types.SMALLINT: {
- return toShort(value);
- }
- case Types.NUMERIC: {
- return toBigDecimal(value);
- }
- case Types.DECIMAL: {
- return toBigDecimal(value);
- }
- case Types.REAL: {
- return toFloat(value);
- }
- case Types.DOUBLE: {
- return toDouble(value);
- }
- case Types.FLOAT: {
- return toDouble(value);
- }
- case Types.BOOLEAN: {
- return toBoolean(value);
- }
- case Types.TIMESTAMP: {
- return toTimestamp(value);
- }
- case Types.DATE: {
- return toDate(value);
- }
- case Types.VARCHAR: {
- return toString(value);
- }
- case Types.CHAR: {
- return toString(value);
- }
- case Types.OTHER: {
- return value;
- }
- case Types.JAVA_OBJECT: {
- return value;
- }
- case Types.BINARY:
- case Types.LONGVARBINARY:
- case Types.BLOB: {
- return value;
- }
- case Types.LONGVARCHAR:
- case Types.CLOB: {
- return value;
- }
- default: {
- String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
- throw new RuntimeException(msg);
- }
- }
- } catch (ClassCastException e) {
- String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
- throw new RuntimeException(m);
- }
- }
-
- /**
- * Convert the value to a String.
- */
- public static String toString(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof String) {
- return (String) value;
- }
- if (value instanceof char[]) {
- return String.valueOf((char[]) value);
- }
-
- return value.toString();
- }
-
-
- /**
- * Convert the value to a Boolean with an explicit String true value.
- */
- public static Boolean toBoolean(Object value, String dbTrueValue) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Boolean) {
- return (Boolean) value;
- }
- String s = value.toString();
- return s.equalsIgnoreCase(dbTrueValue);
- }
-
- /**
- * Convert the value to a Boolean. Can be a Boolean or the string values
- * "true" or "false".
- */
- public static Boolean toBoolean(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Boolean) {
- return (Boolean) value;
- }
-
- return Boolean.valueOf(value.toString());
- }
-
- /**
- * Convert the value to a UUID.
- */
- public static UUID toUUID(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof String) {
- return UUID.fromString((String) value);
- }
- return (UUID) value;
- }
-
- /**
- * convert the passed in object to a BigDecimal. It should be another
- * numeric type.
- */
- public static BigDecimal toBigDecimal(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof BigDecimal) {
- return (BigDecimal) value;
- }
- return new BigDecimal(value.toString());
- }
-
- public static Float toFloat(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Float) {
- return (Float) value;
- }
- if (value instanceof Number) {
- return Float.valueOf(((Number) value).floatValue());
- }
- return Float.valueOf(value.toString());
- }
-
- public static Short toShort(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Short) {
- return (Short) value;
- }
- if (value instanceof Number) {
- return Short.valueOf(((Number) value).shortValue());
- }
- return Short.valueOf(value.toString());
- }
-
- public static Byte toByte(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Byte) {
- return (Byte) value;
- }
- return Byte.valueOf(value.toString());
- }
-
- /**
- * convert the passed in object to a Integer. It should be another numeric
- * type.
- */
- public static Integer toInteger(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Integer) {
- return (Integer) value;
- }
- if (value instanceof Number) {
- return Integer.valueOf(((Number) value).intValue());
- }
- return Integer.valueOf(value.toString());
- }
-
- /**
- * Convert the object to a Long. It should be another numeric type.
- */
- public static Long toLong(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Long) {
- return (Long) value;
- }
- if (value instanceof String) {
- return Long.valueOf((String) value);
- }
- if (value instanceof Number) {
- return Long.valueOf(((Number) value).longValue());
- }
- if (value instanceof java.util.Date) {
- return Long.valueOf(((java.util.Date) value).getTime());
- }
- if (value instanceof Calendar) {
- return Long.valueOf(((Calendar) value).getTime().getTime());
- }
- return Long.valueOf(value.toString());
- }
-
- public static BigInteger toMathBigInteger(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof BigInteger) {
- return (BigInteger) value;
- }
- return new BigInteger(value.toString());
- }
-
- /**
- * Convert the object to a Double. It should be another numberic type.
- */
- public static Double toDouble(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Double) {
- return (Double) value;
- }
- if (value instanceof Number) {
- return Double.valueOf(((Number) value).doubleValue());
- }
- return Double.valueOf(value.toString());
- }
-
- /**
- * convert the passed in object to a Timestamp. It is expected to be a
- * java.sql.Date really.
- */
- public static Timestamp toTimestamp(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Timestamp) {
- return (Timestamp) value;
-
- } else if (value instanceof java.util.Date) {
- // no nanos here... so hopefully ok
- return new Timestamp(((java.util.Date) value).getTime());
-
- } else if (value instanceof Calendar) {
- return new Timestamp(((Calendar) value).getTime().getTime());
-
- } else if (value instanceof String) {
- return Timestamp.valueOf((String) value);
-
- } else if (value instanceof Number) {
- return new Timestamp(((Number) value).longValue());
-
- } else {
- String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
- throw new RuntimeException(msg);
- }
- }
-
- public static java.sql.Time toTime(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof java.sql.Time) {
- return (java.sql.Time) value;
-
- } else if (value instanceof String) {
- return java.sql.Time.valueOf((String) value);
-
- } else {
- String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
- throw new RuntimeException(m);
- }
- }
-
- /**
- * convert the passed in object to a java sql Date.
- */
- public static java.sql.Date toDate(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof java.sql.Date) {
- return (java.sql.Date) value;
-
- } else if (value instanceof java.util.Date) {
- return new java.sql.Date(((java.util.Date) value).getTime());
-
- } else if (value instanceof Calendar) {
- return new java.sql.Date(((Calendar) value).getTime().getTime());
-
- } else if (value instanceof String) {
- return java.sql.Date.valueOf((String) value);
-
- } else if (value instanceof Number) {
- return new java.sql.Date(((Number) value).longValue());
-
- } else {
- String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
- throw new RuntimeException(m);
- }
- }
-
- /**
- * convert the passed in object to a java sql Date.
- */
- public static java.util.Date toUtilDate(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof java.sql.Timestamp) {
- // loss of nanos precision
- return new java.util.Date(((java.sql.Timestamp) value).getTime());
- }
- // DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
- // java.util.Date? equals() is symmetrical so perhaps this is not
- // really required?
- if (value instanceof java.sql.Date) {
- return new java.util.Date(((java.sql.Date) value).getTime());
- }
- if (value instanceof java.util.Date) {
- return (java.util.Date) value;
-
- } else if (value instanceof Calendar) {
- return ((Calendar) value).getTime();
-
- } else if (value instanceof String) {
- return new java.util.Date(Timestamp.valueOf((String) value).getTime());
-
- } else if (value instanceof Number) {
- return new java.util.Date(((Number) value).longValue());
-
- } else {
- throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
- }
- }
-
- /**
- * convert the passed in object to a java sql Date.
- */
- public static Calendar toCalendar(Object value) {
-
- if (value == null) {
- return null;
- }
- if (value instanceof Calendar) {
- return (Calendar) value;
-
- } else if (value instanceof java.util.Date) {
- java.util.Date date = ((java.util.Date) value);
- return toCalendarFromDate(date);
-
- } else if (value instanceof String) {
- java.util.Date date = toUtilDate(value);
- return toCalendarFromDate(date);
-
- } else if (value instanceof Number) {
- long timeMillis = ((Number) value).longValue();
- java.util.Date date = new java.util.Date(timeMillis);
- return toCalendarFromDate(date);
-
- } else {
- String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
- throw new RuntimeException(m);
- }
- }
-
- private static Calendar toCalendarFromDate(java.util.Date date) {
-
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
-
- return cal;
- }
-
-}
+package com.avaje.ebeaninternal.server.core;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+import java.util.UUID;
+
+
+/**
+ * Default implementation of TypeConverter.
+ *
+ * Converts objects to the required type if required.
+ *
+ */
+public final class BasicTypeConverter implements Serializable {
+
+ private static final long serialVersionUID = 7691463236204070311L;
+
+ /**
+ * Type code for java.util.Calendar.
+ */
+ public static final int UTIL_CALENDAR = -999998986;
+
+ /**
+ * Type code for java.util.Date.
+ */
+ public static final int UTIL_DATE = -999998988;
+
+ /**
+ * Type code for java.math.BigInteger.
+ */
+ public static final int MATH_BIGINTEGER = -999998987;
+
+ /**
+ * Type code for an Enum type.
+ */
+ public static final int ENUM = -999998989;
+
+ private BasicTypeConverter() {
+ }
+
+ /**
+ * Convert the Object to the required data type.
+ *
+ * @param value
+ * the Object value
+ * @param toDataType
+ * the dataType as per java.sql.Types.
+ */
+ public static Object convert(Object value, int toDataType) {
+
+ try {
+ switch (toDataType) {
+ case UTIL_DATE: {
+ return toUtilDate(value);
+ }
+ case UTIL_CALENDAR: {
+ return toCalendar(value);
+ }
+ case Types.BIGINT: {
+ return toLong(value);
+ }
+ case Types.INTEGER: {
+ return toInteger(value);
+ }
+ case Types.BIT: {
+ return toBoolean(value);
+ }
+ case Types.TINYINT: {
+ return toByte(value);
+ }
+ case Types.SMALLINT: {
+ return toShort(value);
+ }
+ case Types.NUMERIC: {
+ return toBigDecimal(value);
+ }
+ case Types.DECIMAL: {
+ return toBigDecimal(value);
+ }
+ case Types.REAL: {
+ return toFloat(value);
+ }
+ case Types.DOUBLE: {
+ return toDouble(value);
+ }
+ case Types.FLOAT: {
+ return toDouble(value);
+ }
+ case Types.BOOLEAN: {
+ return toBoolean(value);
+ }
+ case Types.TIMESTAMP: {
+ return toTimestamp(value);
+ }
+ case Types.DATE: {
+ return toDate(value);
+ }
+ case Types.VARCHAR: {
+ return toString(value);
+ }
+ case Types.CHAR: {
+ return toString(value);
+ }
+ case Types.OTHER: {
+ return value;
+ }
+ case Types.JAVA_OBJECT: {
+ return value;
+ }
+ case Types.BINARY:
+ case Types.LONGVARBINARY:
+ case Types.BLOB: {
+ return value;
+ }
+ case Types.LONGVARCHAR:
+ case Types.CLOB: {
+ return value;
+ }
+ default: {
+ String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
+ throw new RuntimeException(msg);
+ }
+ }
+ } catch (ClassCastException e) {
+ String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
+ throw new RuntimeException(m);
+ }
+ }
+
+ /**
+ * Convert the value to a String.
+ */
+ public static String toString(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return (String) value;
+ }
+ if (value instanceof char[]) {
+ return String.valueOf((char[]) value);
+ }
+
+ return value.toString();
+ }
+
+
+ /**
+ * Convert the value to a Boolean with an explicit String true value.
+ */
+ public static Boolean toBoolean(Object value, String dbTrueValue) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+ String s = value.toString();
+ return s.equalsIgnoreCase(dbTrueValue);
+ }
+
+ /**
+ * Convert the value to a Boolean. Can be a Boolean or the string values
+ * "true" or "false".
+ */
+ public static Boolean toBoolean(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ return Boolean.valueOf(value.toString());
+ }
+
+ /**
+ * Convert the value to a UUID.
+ */
+ public static UUID toUUID(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return UUID.fromString((String) value);
+ }
+ return (UUID) value;
+ }
+
+ /**
+ * convert the passed in object to a BigDecimal. It should be another
+ * numeric type.
+ */
+ public static BigDecimal toBigDecimal(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof BigDecimal) {
+ return (BigDecimal) value;
+ }
+ return new BigDecimal(value.toString());
+ }
+
+ public static Float toFloat(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Float) {
+ return (Float) value;
+ }
+ if (value instanceof Number) {
+ return Float.valueOf(((Number) value).floatValue());
+ }
+ return Float.valueOf(value.toString());
+ }
+
+ public static Short toShort(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Short) {
+ return (Short) value;
+ }
+ if (value instanceof Number) {
+ return Short.valueOf(((Number) value).shortValue());
+ }
+ return Short.valueOf(value.toString());
+ }
+
+ public static Byte toByte(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Byte) {
+ return (Byte) value;
+ }
+ return Byte.valueOf(value.toString());
+ }
+
+ /**
+ * convert the passed in object to a Integer. It should be another numeric
+ * type.
+ */
+ public static Integer toInteger(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Integer) {
+ return (Integer) value;
+ }
+ if (value instanceof Number) {
+ return Integer.valueOf(((Number) value).intValue());
+ }
+ return Integer.valueOf(value.toString());
+ }
+
+ /**
+ * Convert the object to a Long. It should be another numeric type.
+ */
+ public static Long toLong(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Long) {
+ return (Long) value;
+ }
+ if (value instanceof String) {
+ return Long.valueOf((String) value);
+ }
+ if (value instanceof Number) {
+ return Long.valueOf(((Number) value).longValue());
+ }
+ if (value instanceof java.util.Date) {
+ return Long.valueOf(((java.util.Date) value).getTime());
+ }
+ if (value instanceof Calendar) {
+ return Long.valueOf(((Calendar) value).getTime().getTime());
+ }
+ return Long.valueOf(value.toString());
+ }
+
+ public static BigInteger toMathBigInteger(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof BigInteger) {
+ return (BigInteger) value;
+ }
+ return new BigInteger(value.toString());
+ }
+
+ /**
+ * Convert the object to a Double. It should be another numberic type.
+ */
+ public static Double toDouble(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Double) {
+ return (Double) value;
+ }
+ if (value instanceof Number) {
+ return Double.valueOf(((Number) value).doubleValue());
+ }
+ return Double.valueOf(value.toString());
+ }
+
+ /**
+ * convert the passed in object to a Timestamp. It is expected to be a
+ * java.sql.Date really.
+ */
+ public static Timestamp toTimestamp(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Timestamp) {
+ return (Timestamp) value;
+
+ } else if (value instanceof java.util.Date) {
+ // no nanos here... so hopefully ok
+ return new Timestamp(((java.util.Date) value).getTime());
+
+ } else if (value instanceof Calendar) {
+ return new Timestamp(((Calendar) value).getTime().getTime());
+
+ } else if (value instanceof String) {
+ return Timestamp.valueOf((String) value);
+
+ } else if (value instanceof Number) {
+ return new Timestamp(((Number) value).longValue());
+
+ } else {
+ String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
+ throw new RuntimeException(msg);
+ }
+ }
+
+ public static java.sql.Time toTime(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof java.sql.Time) {
+ return (java.sql.Time) value;
+
+ } else if (value instanceof String) {
+ return java.sql.Time.valueOf((String) value);
+
+ } else {
+ String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
+ throw new RuntimeException(m);
+ }
+ }
+
+ /**
+ * convert the passed in object to a java sql Date.
+ */
+ public static java.sql.Date toDate(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof java.sql.Date) {
+ return (java.sql.Date) value;
+
+ } else if (value instanceof java.util.Date) {
+ return new java.sql.Date(((java.util.Date) value).getTime());
+
+ } else if (value instanceof Calendar) {
+ return new java.sql.Date(((Calendar) value).getTime().getTime());
+
+ } else if (value instanceof String) {
+ return java.sql.Date.valueOf((String) value);
+
+ } else if (value instanceof Number) {
+ return new java.sql.Date(((Number) value).longValue());
+
+ } else {
+ String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
+ throw new RuntimeException(m);
+ }
+ }
+
+ /**
+ * convert the passed in object to a java sql Date.
+ */
+ public static java.util.Date toUtilDate(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof java.sql.Timestamp) {
+ // loss of nanos precision
+ return new java.util.Date(((java.sql.Timestamp) value).getTime());
+ }
+ // DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
+ // java.util.Date? equals() is symmetrical so perhaps this is not
+ // really required?
+ if (value instanceof java.sql.Date) {
+ return new java.util.Date(((java.sql.Date) value).getTime());
+ }
+ if (value instanceof java.util.Date) {
+ return (java.util.Date) value;
+
+ } else if (value instanceof Calendar) {
+ return ((Calendar) value).getTime();
+
+ } else if (value instanceof String) {
+ return new java.util.Date(Timestamp.valueOf((String) value).getTime());
+
+ } else if (value instanceof Number) {
+ return new java.util.Date(((Number) value).longValue());
+
+ } else {
+ throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
+ }
+ }
+
+ /**
+ * convert the passed in object to a java sql Date.
+ */
+ public static Calendar toCalendar(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Calendar) {
+ return (Calendar) value;
+
+ } else if (value instanceof java.util.Date) {
+ java.util.Date date = ((java.util.Date) value);
+ return toCalendarFromDate(date);
+
+ } else if (value instanceof String) {
+ java.util.Date date = toUtilDate(value);
+ return toCalendarFromDate(date);
+
+ } else if (value instanceof Number) {
+ long timeMillis = ((Number) value).longValue();
+ java.util.Date date = new java.util.Date(timeMillis);
+ return toCalendarFromDate(date);
+
+ } else {
+ String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
+ throw new RuntimeException(m);
+ }
+ }
+
+ private static Calendar toCalendarFromDate(java.util.Date date) {
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+
+ return cal;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java
index 3632c7081..f19c5c841 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java
@@ -1,146 +1,127 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.core;
-
-import java.sql.Connection;
-
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.LogLevel;
-import com.avaje.ebeaninternal.api.SpiEbeanServer;
-import com.avaje.ebeaninternal.api.SpiTransaction;
-
-/**
- * Base class for find and persist requests.
- */
-public abstract class BeanRequest {
-
- /**
- * The server processing the request.
- */
- final SpiEbeanServer ebeanServer;
-
- final String serverName;
-
- /**
- * The transaction this is part of.
- */
- SpiTransaction transaction;
-
- boolean createdTransaction;
-
- boolean readOnly;
-
- public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
- this.ebeanServer = ebeanServer;
- this.serverName = ebeanServer.getName();
- this.transaction = t;
- }
-
- /**
- * initialise an implicit transaction if one is not currently supplied.
- *
- * A transaction may have been passed in or active in the thread local. If
- * not then create one implicitly to handle the request.
- *
- */
- public abstract void initTransIfRequired();
-
- /**
- * A helper method for creating an implicit transaction is it is required.
- *
- * A transaction may have been passed in or active in the thread local. If
- * not then create one implicitly to handle the request.
- *
- */
- public void createImplicitTransIfRequired(boolean readOnlyTransaction) {
- if (transaction == null) {
- transaction = ebeanServer.getCurrentServerTransaction();
- if (transaction == null || !transaction.isActive()) {
- // create an implicit transaction to execute this query
- transaction = ebeanServer.createServerTransaction(false, -1);
- // commented out for performance reasons...
- // TODO: review performance of trans.setReadOnly(true)
- //if (readOnlyTransaction) {
- // readOnly = true;
- // transaction.setReadOnly(true);
- //}
- createdTransaction = true;
- }
- }
- }
-
- /**
- * Commit this transaction if it was created for this request.
- */
- public void commitTransIfRequired() {
- if (createdTransaction) {
- if (readOnly) {
- transaction.rollback();
- } else {
- transaction.commit();
- }
- }
- }
-
- /**
- * Rollback the transaction if it was created for this request.
- */
- public void rollbackTransIfRequired() {
- if (createdTransaction) {
- transaction.rollback();
- }
- }
-
- /**
- * Return the server processing the request. Made available for
- * BeanController and BeanFinder.
- */
- public EbeanServer getEbeanServer() {
- return ebeanServer;
- }
-
- /**
- * Return the Transaction associated with this request.
- */
- public SpiTransaction getTransaction() {
- return transaction;
- }
-
- /**
- * Returns the connection from the Transaction.
- */
- public Connection getConnection() {
- return transaction.getInternalConnection();
- }
-
- /**
- * Return true if SQL should be logged for this transaction.
- */
- public boolean isLogSql() {
- return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
- }
-
- /**
- * Return true if SUMMARY information should be logged for this transaction.
- */
- public boolean isLogSummary() {
- return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
- }
-}
+package com.avaje.ebeaninternal.server.core;
+
+import java.sql.Connection;
+
+import com.avaje.ebean.EbeanServer;
+import com.avaje.ebean.LogLevel;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.api.SpiTransaction;
+
+/**
+ * Base class for find and persist requests.
+ */
+public abstract class BeanRequest {
+
+ /**
+ * The server processing the request.
+ */
+ final SpiEbeanServer ebeanServer;
+
+ final String serverName;
+
+ /**
+ * The transaction this is part of.
+ */
+ SpiTransaction transaction;
+
+ boolean createdTransaction;
+
+ boolean readOnly;
+
+ public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
+ this.ebeanServer = ebeanServer;
+ this.serverName = ebeanServer.getName();
+ this.transaction = t;
+ }
+
+ /**
+ * initialise an implicit transaction if one is not currently supplied.
+ *
+ * A transaction may have been passed in or active in the thread local. If
+ * not then create one implicitly to handle the request.
+ *
+ */
+ public abstract void initTransIfRequired();
+
+ /**
+ * A helper method for creating an implicit transaction is it is required.
+ *
+ * A transaction may have been passed in or active in the thread local. If
+ * not then create one implicitly to handle the request.
+ *
+ */
+ public void createImplicitTransIfRequired(boolean readOnlyTransaction) {
+ if (transaction == null) {
+ transaction = ebeanServer.getCurrentServerTransaction();
+ if (transaction == null || !transaction.isActive()) {
+ // create an implicit transaction to execute this query
+ transaction = ebeanServer.createServerTransaction(false, -1);
+ // commented out for performance reasons...
+ // TODO: review performance of trans.setReadOnly(true)
+ //if (readOnlyTransaction) {
+ // readOnly = true;
+ // transaction.setReadOnly(true);
+ //}
+ createdTransaction = true;
+ }
+ }
+ }
+
+ /**
+ * Commit this transaction if it was created for this request.
+ */
+ public void commitTransIfRequired() {
+ if (createdTransaction) {
+ if (readOnly) {
+ transaction.rollback();
+ } else {
+ transaction.commit();
+ }
+ }
+ }
+
+ /**
+ * Rollback the transaction if it was created for this request.
+ */
+ public void rollbackTransIfRequired() {
+ if (createdTransaction) {
+ transaction.rollback();
+ }
+ }
+
+ /**
+ * Return the server processing the request. Made available for
+ * BeanController and BeanFinder.
+ */
+ public EbeanServer getEbeanServer() {
+ return ebeanServer;
+ }
+
+ /**
+ * Return the Transaction associated with this request.
+ */
+ public SpiTransaction getTransaction() {
+ return transaction;
+ }
+
+ /**
+ * Returns the connection from the Transaction.
+ */
+ public Connection getConnection() {
+ return transaction.getInternalConnection();
+ }
+
+ /**
+ * Return true if SQL should be logged for this transaction.
+ */
+ public boolean isLogSql() {
+ return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
+ }
+
+ /**
+ * Return true if SUMMARY information should be logged for this transaction.
+ */
+ public boolean isLogSummary() {
+ return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java
index 140973c58..6aa927f67 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java
@@ -1,22 +1,3 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
package com.avaje.ebeaninternal.server.core;
import java.util.List;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java
index 197d6a7c8..e16d161c8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java
@@ -1,460 +1,441 @@
-/**
- * Copyright (C) 2006 Robin Bygrave
- *
- * This file is part of Ebean.
- *
- * Ebean is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * Ebean is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Ebean; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-package com.avaje.ebeaninternal.server.core;
-
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.persistence.Embeddable;
-import javax.persistence.Entity;
-import javax.persistence.Table;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-
-import com.avaje.ebean.annotation.LdapDomain;
-import com.avaje.ebean.config.CompoundType;
-import com.avaje.ebean.config.ScalarTypeConverter;
-import com.avaje.ebean.config.ServerConfig;
-import com.avaje.ebean.event.BeanFinder;
-import com.avaje.ebean.event.BeanPersistController;
-import com.avaje.ebean.event.BeanPersistListener;
-import com.avaje.ebean.event.BeanQueryAdapter;
-import com.avaje.ebean.event.ServerConfigStartup;
-import com.avaje.ebean.event.TransactionEventListener;
-import com.avaje.ebeaninternal.server.type.ScalarType;
-import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
-
-/**
- * Interesting classes for a EbeanServer such as Embeddable, Entity,
- * ScalarTypes, Finders, Listeners and Controllers.
- */
-public class BootupClasses implements ClassPathSearchMatcher {
-
- private static final Logger logger = Logger.getLogger(BootupClasses.class.getName());
-
- private ArrayList> xmlBeanList = new ArrayList>();
-
- private ArrayList> embeddableList = new ArrayList>();
-
- private ArrayList> entityList = new ArrayList>();
-
- private ArrayList> scalarTypeList = new ArrayList>();
-
- private ArrayList> scalarConverterList = new ArrayList>();
-
- private ArrayList> compoundTypeList = new ArrayList>();
-
- private ArrayList> beanControllerList = new ArrayList>();
-
- private ArrayList> transactionEventListenerList = new ArrayList>();
-
- private ArrayList> beanFinderList = new ArrayList>();
-
- private ArrayList> beanListenerList = new ArrayList>();
-
- private ArrayList