From 39520e367d11a551ab3d96981593dbe2b1aedca9 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Sat, 9 May 2015 01:07:41 +1200 Subject: [PATCH] No effective change - change newline char --- .../server/cluster/BinaryMessage.java | 120 +- .../server/cluster/BinaryMessageList.java | 46 +- .../server/cluster/ClusterBroadcast.java | 56 +- .../server/cluster/ClusterManager.java | 198 +-- .../server/cluster/DataHolder.java | 48 +- .../ebeaninternal/server/cluster/Packet.java | 386 ++--- .../server/cluster/PacketMessages.java | 138 +- .../cluster/PacketTransactionEvent.java | 138 +- .../cluster/mcast/AckResendMessages.java | 86 +- .../cluster/mcast/IncomingPacketsLastAck.java | 106 +- .../mcast/IncomingPacketsProcessed.java | 550 +++---- .../server/cluster/mcast/McastSender.java | 232 +-- .../server/cluster/mcast/McastStatus.java | 272 ++-- .../server/cluster/mcast/Message.java | 28 +- .../server/cluster/mcast/MessageAck.java | 114 +- .../server/cluster/mcast/MessageControl.java | 146 +- .../server/cluster/mcast/MessageResend.java | 154 +- .../cluster/mcast/OutgoingPacketsAcked.java | 188 +-- .../cluster/mcast/OutgoingPacketsCache.java | 132 +- .../cluster/socket/RequestProcessor.java | 120 +- .../server/cluster/socket/SocketClient.java | 278 ++-- .../socket/SocketClusterBroadcast.java | 512 +++--- .../cluster/socket/SocketClusterListener.java | 286 ++-- .../cluster/socket/SocketClusterMessage.java | 156 +- .../cluster/socket/SocketClusterStatus.java | 82 +- .../cluster/socket/SocketConnection.java | 258 +-- .../server/core/BasicTypeConverter.java | 956 +++++------ .../server/core/BeanRequest.java | 264 +-- .../server/core/BootupClasses.java | 784 ++++----- .../server/core/DatabasePlatformFactory.java | 332 ++-- .../core/DefaultBackgroundExecutor.java | 104 +- .../server/core/DefaultBeanLoader.java | 884 +++++----- .../server/core/DefaultCallableSql.java | 246 +-- .../server/core/DefaultSqlUpdate.java | 482 +++--- .../ebeaninternal/server/core/DiffHelp.java | 296 ++-- .../server/core/InternalConfiguration.java | 536 +++---- .../server/core/JndiDataSourceLookup.java | 88 +- .../ebeaninternal/server/core/Message.java | 128 +- .../core/OnBootupClassSearchMatcher.java | 44 +- .../server/core/OrmQueryEngine.java | 76 +- .../server/core/OrmQueryRequest.java | 830 +++++----- .../server/core/PersistRequest.java | 216 +-- .../server/core/PersistRequestBean.java | 1418 ++++++++--------- .../core/PersistRequestCallableSql.java | 294 ++-- .../server/core/PersistRequestOrmUpdate.java | 238 +-- .../server/core/PersistRequestUpdateSql.java | 252 +-- .../ebeaninternal/server/core/Persister.java | 186 +-- .../ebeaninternal/server/core/PstmtBatch.java | 34 +- .../server/core/RefreshHelp.java | 430 ++--- .../server/core/RelationalQueryRequest.java | 228 +-- .../server/core/ServletContextListener.java | 62 +- .../server/core/SpiOrmQueryRequest.java | 228 +-- .../ebeaninternal/server/core/XmlConfig.java | 128 +- .../server/core/XmlConfigLoader.java | 142 +- 54 files changed, 7368 insertions(+), 7368 deletions(-) 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 31b5233e5..72ed1f323 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java @@ -1,60 +1,60 @@ -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. - *

- */ -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_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. + *

+ */ +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_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 3c0e36fe0..0c89a1611 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java @@ -1,23 +1,23 @@ -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 de871602c..bfcb47720 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java @@ -1,28 +1,28 @@ -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 f78aca445..5e8ae4dd9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java @@ -1,99 +1,99 @@ -package com.avaje.ebeaninternal.server.cluster; - -import java.util.concurrent.ConcurrentHashMap; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.config.ContainerConfig; -import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager; -import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Manages the cluster service. - */ -public class ClusterManager { - - private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class); - - private final ConcurrentHashMap serverMap = new ConcurrentHashMap(); - - private final Object monitor = new Object(); - - private final ClusterBroadcast broadcast; - - private boolean started; - - public ClusterManager(ContainerConfig containerConfig) { - - ContainerConfig.ClusterMode mode = containerConfig.getMode(); - try { - switch (mode) { - case SOCKET: { - this.broadcast = new SocketClusterBroadcast(containerConfig); - break; - } - case MULTICAST: { - this.broadcast = new McastClusterManager(containerConfig); - break; - } - default: { - this.broadcast = null; - } - } - - } catch (Exception e) { - logger.error("Error initialising ClusterManager type [" + mode + "]", e); - throw new RuntimeException(e); - } - } - - public void registerServer(EbeanServer server) { - synchronized (monitor) { - serverMap.put(server.getName(), server); - if (!started) { - startup(); - } - } - } - - 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 com.avaje.ebean.EbeanServer; +import com.avaje.ebean.config.ContainerConfig; +import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager; +import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages the cluster service. + */ +public class ClusterManager { + + private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class); + + private final ConcurrentHashMap serverMap = new ConcurrentHashMap(); + + private final Object monitor = new Object(); + + private final ClusterBroadcast broadcast; + + private boolean started; + + public ClusterManager(ContainerConfig containerConfig) { + + ContainerConfig.ClusterMode mode = containerConfig.getMode(); + try { + switch (mode) { + case SOCKET: { + this.broadcast = new SocketClusterBroadcast(containerConfig); + break; + } + case MULTICAST: { + this.broadcast = new McastClusterManager(containerConfig); + break; + } + default: { + this.broadcast = null; + } + } + + } catch (Exception e) { + logger.error("Error initialising ClusterManager type [" + mode + "]", e); + throw new RuntimeException(e); + } + } + + public void registerServer(EbeanServer server) { + synchronized (monitor) { + serverMap.put(server.getName(), server); + if (!started) { + startup(); + } + } + } + + 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 082ccded8..c38cf7a9a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java @@ -1,24 +1,24 @@ -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 d8fd083a6..f7466c68f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java @@ -1,193 +1,193 @@ -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 9dac59a44..ab8fd5fa7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java @@ -1,69 +1,69 @@ -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 9044caf2b..5f44c23cd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java @@ -1,69 +1,69 @@ -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.RemoteTransactionEvent; - -/** - * A Packet holding TransactionEvent data. - *

- * Due to the hard limit for UDP packet sizes a RemoteTransactionEvent - * is actually broken up into smaller messages. - *

- */ -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; - - 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.RemoteTransactionEvent; + +/** + * A Packet holding TransactionEvent data. + *

+ * Due to the hard limit for UDP packet sizes a RemoteTransactionEvent + * is actually broken up into smaller messages. + *

+ */ +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; + + default: + throw new RuntimeException("Invalid Transaction msgType "+msgType); + } + } + +} 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 b35d8e6c5..8483da8a7 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,43 +1,43 @@ -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 882e451cf..0f688ab5a 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,53 +1,53 @@ -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 20af49880..a024da601 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,275 +1,275 @@ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 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 = LoggerFactory.getLogger(GotAllPoint.class); - - 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.warn("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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 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 = LoggerFactory.getLogger(GotAllPoint.class); + + 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.warn("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/McastSender.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java index 1e6214033..452eaf5cf 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,116 +1,116 @@ -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 com.avaje.ebeaninternal.server.cluster.Packet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Handles the sending of Packets via DatagramPacket. - * - * @author rbygrave - */ -public class McastSender { - - private static final Logger logger = LoggerFactory.getLogger(McastSender.class); - - 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.isDebugEnabled()){ - logger.debug("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length); - } - - if (pktBytes.length > 65507){ - logger.warn("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 com.avaje.ebeaninternal.server.cluster.Packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles the sending of Packets via DatagramPacket. + * + * @author rbygrave + */ +public class McastSender { + + private static final Logger logger = LoggerFactory.getLogger(McastSender.class); + + 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.isDebugEnabled()){ + logger.debug("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length); + } + + if (pktBytes.length > 65507){ + logger.warn("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 bc5c8e8db..993b65e11 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,136 +1,136 @@ -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 f6a2fc5b6..42718418e 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,14 +1,14 @@ -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 6266c0dd5..53cf1c93c 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,57 +1,57 @@ -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 d5494d3aa..2c2053ac3 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,73 +1,73 @@ -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 700c8a2a3..00d6e1cb3 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,77 +1,77 @@ -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 f0bc221fb..8e6d9c43b 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,94 +1,94 @@ -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 = Long.MAX_VALUE; - - for (GroupMemberAck groupMemAck : recievedByMap.values()) { - long memberMin = groupMemAck.getGotAllPacketId(); - if (memberMin < tempMin) { - 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) { - groupMemberAck = new GroupMemberAck(); - groupMemberAck.setIfBigger(ack.getGotAllPacketId()); - recievedByMap.put(groupMember, groupMemberAck); - checkMin = true; - } else { - checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId; - groupMemberAck.setIfBigger(ack.getGotAllPacketId()); - } - - boolean minChanged = false; - - if (checkMin || minimumGotAllPacketId == 0){ - minChanged = resetGotAllMin(); - } - - 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 = Long.MAX_VALUE; + + for (GroupMemberAck groupMemAck : recievedByMap.values()) { + long memberMin = groupMemAck.getGotAllPacketId(); + if (memberMin < tempMin) { + 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) { + groupMemberAck = new GroupMemberAck(); + groupMemberAck.setIfBigger(ack.getGotAllPacketId()); + recievedByMap.put(groupMember, groupMemberAck); + checkMin = true; + } else { + checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId; + groupMemberAck.setIfBigger(ack.getGotAllPacketId()); + } + + boolean minChanged = false; + + if (checkMin || minimumGotAllPacketId == 0){ + minChanged = resetGotAllMin(); + } + + 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 be5c103c3..09213c993 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,66 +1,66 @@ -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 4b2176a8b..43a67e1b2 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,61 +1,61 @@ -package com.avaje.ebeaninternal.server.cluster.socket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.Socket; - -/** - * 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 = LoggerFactory.getLogger(RequestProcessor.class); - - private final Socket clientSocket; - - private final SocketClusterBroadcast owner; - - private final String hostPort; - - /** - * 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.hostPort = owner.getHostPort(); - } - - /** - * 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 { - logger.trace("start listening for cluster messages"); - SocketConnection sc = new SocketConnection(clientSocket); - while (true) { - if (owner.process(sc)) { - // got the offline message or timeout - break; - } - } - logger.trace("disconnecting: {}", hostPort); - sc.disconnect(); - - } catch (Exception e) { - logger.error("Error listening for messages - "+owner.getHostPort(), e); - } - } - +package com.avaje.ebeaninternal.server.cluster.socket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.Socket; + +/** + * 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 = LoggerFactory.getLogger(RequestProcessor.class); + + private final Socket clientSocket; + + private final SocketClusterBroadcast owner; + + private final String hostPort; + + /** + * 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.hostPort = owner.getHostPort(); + } + + /** + * 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 { + logger.trace("start listening for cluster messages"); + SocketConnection sc = new SocketConnection(clientSocket); + while (true) { + if (owner.process(sc)) { + // got the offline message or timeout + break; + } + } + logger.trace("disconnecting: {}", hostPort); + sc.disconnect(); + + } catch (Exception e) { + logger.error("Error listening for messages - "+owner.getHostPort(), e); + } + } + } \ No newline at end of file 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 1dde45140..db5b47aa8 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,139 +1,139 @@ -package com.avaje.ebeaninternal.server.cluster.socket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; - - -/** - * The client side of the socket clustering. - */ -class SocketClient { - - private static final Logger logger = LoggerFactory.getLogger(SocketClient.class); - - 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 toString() { - return address.toString(); - } - - 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.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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; + + +/** + * The client side of the socket clustering. + */ +class SocketClient { + + private static final Logger logger = LoggerFactory.getLogger(SocketClient.class); + + 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 toString() { + return address.toString(); + } + + 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.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 d96bb7ef0..3b73b6e3a 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,256 +1,256 @@ -package com.avaje.ebeaninternal.server.cluster.socket; - -import com.avaje.ebean.config.ContainerConfig; -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.transaction.RemoteTransactionEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.EOFException; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Broadcast messages across the cluster using sockets. - */ -public class SocketClusterBroadcast implements ClusterBroadcast { - - private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class); - - 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(ContainerConfig containerConfig) { - - ContainerConfig.SocketConfig socketConfig = containerConfig.getSocketConfig(); - - String localHostPort = socketConfig.getLocalHostPort(); - List members = socketConfig.getMembers(); - - logger.info("Clustering using Sockets local[" + localHostPort + "] members[" + members + "]"); - - this.local = new SocketClient(parseFullName(localHostPort)); - this.clientMap = new HashMap(); - - for (String memberHostPort : members) { - InetSocketAddress member = parseFullName(memberHostPort); - 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(), socketConfig.getCoreThreads(), socketConfig.getMaxThreads(), socketConfig.getThreadPoolName()); - } - - public String getHostPort() { - return local.getHostPort(); - } - - /** - * 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); - logger.info("Cluster Member [{}] online[{}]", members[i].getHostPort(), online); - } - } - - protected void setMemberOnline(String fullName, boolean online) throws IOException { - synchronized (clientMap) { - logger.info("Cluster Member [{}] online[{}]", fullName, online); - 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 - if (logger.isTraceEnabled()) { - logger.trace("... send to member {} broadcast msg: {}", client, msg); - } - client.send(msg); - - } catch (Exception ex) { - logger.error("Error sending message", ex); - try { - client.reconnect(); - } catch (IOException e) { - logger.error("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) { - logger.error("Error sending RemoteTransactionEvent " + remoteTransEvent + " to cluster members.", e); - } - } - - protected void broadcast(SocketClusterMessage msg) { - - if (logger.isTraceEnabled()) { - logger.trace("... broadcast msg: "+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 an incoming Cluster message. - */ - protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException { - - try { - SocketClusterMessage h = (SocketClusterMessage) request.readObject(); - if (logger.isTraceEnabled()) { - logger.trace("... received msg: {}", h); - } - - if (h.isRegisterEvent()) { - setMemberOnline(h.getRegisterHost(), h.isRegister()); - - } else { - txnIncoming.incrementAndGet(); - DataHolder dataHolder = h.getDataHolder(); - RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder); - transEvent.run(); - } - - // instance shutting down - return h.isRegisterEvent() && !h.isRegister(); - - } catch (InterruptedIOException e) { - logger.info("Timeout waiting for message", e); - try { - request.disconnect(); - } catch (IOException ex) { - logger.info("Error disconnecting after timeout", ex); - } - return true; - - } catch (EOFException e) { - logger.info("EOF disconnecting"); - return true; - } catch (IOException e) { - logger.info("IO Error waiting/reading message", e); - 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 com.avaje.ebean.config.ContainerConfig; +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.transaction.RemoteTransactionEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.io.EOFException; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Broadcast messages across the cluster using sockets. + */ +public class SocketClusterBroadcast implements ClusterBroadcast { + + private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class); + + 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(ContainerConfig containerConfig) { + + ContainerConfig.SocketConfig socketConfig = containerConfig.getSocketConfig(); + + String localHostPort = socketConfig.getLocalHostPort(); + List members = socketConfig.getMembers(); + + logger.info("Clustering using Sockets local[" + localHostPort + "] members[" + members + "]"); + + this.local = new SocketClient(parseFullName(localHostPort)); + this.clientMap = new HashMap(); + + for (String memberHostPort : members) { + InetSocketAddress member = parseFullName(memberHostPort); + 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(), socketConfig.getCoreThreads(), socketConfig.getMaxThreads(), socketConfig.getThreadPoolName()); + } + + public String getHostPort() { + return local.getHostPort(); + } + + /** + * 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); + logger.info("Cluster Member [{}] online[{}]", members[i].getHostPort(), online); + } + } + + protected void setMemberOnline(String fullName, boolean online) throws IOException { + synchronized (clientMap) { + logger.info("Cluster Member [{}] online[{}]", fullName, online); + 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 + if (logger.isTraceEnabled()) { + logger.trace("... send to member {} broadcast msg: {}", client, msg); + } + client.send(msg); + + } catch (Exception ex) { + logger.error("Error sending message", ex); + try { + client.reconnect(); + } catch (IOException e) { + logger.error("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) { + logger.error("Error sending RemoteTransactionEvent " + remoteTransEvent + " to cluster members.", e); + } + } + + protected void broadcast(SocketClusterMessage msg) { + + if (logger.isTraceEnabled()) { + logger.trace("... broadcast msg: "+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 an incoming Cluster message. + */ + protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException { + + try { + SocketClusterMessage h = (SocketClusterMessage) request.readObject(); + if (logger.isTraceEnabled()) { + logger.trace("... received msg: {}", h); + } + + if (h.isRegisterEvent()) { + setMemberOnline(h.getRegisterHost(), h.isRegister()); + + } else { + txnIncoming.incrementAndGet(); + DataHolder dataHolder = h.getDataHolder(); + RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder); + transEvent.run(); + } + + // instance shutting down + return h.isRegisterEvent() && !h.isRegister(); + + } catch (InterruptedIOException e) { + logger.info("Timeout waiting for message", e); + try { + request.disconnect(); + } catch (IOException ex) { + logger.info("Error disconnecting after timeout", ex); + } + return true; + + } catch (EOFException e) { + logger.info("EOF disconnecting"); + return true; + } catch (IOException e) { + logger.info("IO Error waiting/reading message", e); + 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 c001c5188..88a99fa62 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,143 +1,143 @@ -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 com.avaje.ebeaninternal.server.lib.DaemonThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * 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 = LoggerFactory.getLogger(SocketClusterListener.class); - - /** - * 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 DaemonThreadPool 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, int coreThreads, int maxThreads, String poolName) { - this.owner = owner; - this.threadPool = new DaemonThreadPool(coreThreads, maxThreads, 60, 30, poolName); - try { - this.serverListenSocket = new ServerSocket(port); - this.serverListenSocket.setSoTimeout(60000); - this.listenerThread = new Thread(this, "EbeanClusterListener"); - - } catch (IOException e) { - String msg = "Error starting cluster socket listener on port " + port; - throw new RuntimeException(msg, e); - } - } - - /** - * Start listening for requests. - */ - public void startListening() throws IOException { - logger.trace("... startListening()"); - 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.error("Error shutting down listener", e); - } - - threadPool.shutdown(); - } - - /** - * 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.execute(request); - - isActive = false; - } - } catch (SocketException e) { - if (doingShutdown) { - logger.info("doingShutdown and accept threw:" + e.getMessage()); - } else { - logger.error("Error while listening", e); - } - } catch (InterruptedIOException e) { - // this will happen when the server is very quiet. - // that is, no requests - logger.debug("Possibly expected due to accept timeout? {}", e.getMessage()); - - } catch (IOException e) { - // log it and continue in the loop... - logger.error("IOException processing cluster message", 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 com.avaje.ebeaninternal.server.lib.DaemonThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * 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 = LoggerFactory.getLogger(SocketClusterListener.class); + + /** + * 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 DaemonThreadPool 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, int coreThreads, int maxThreads, String poolName) { + this.owner = owner; + this.threadPool = new DaemonThreadPool(coreThreads, maxThreads, 60, 30, poolName); + try { + this.serverListenSocket = new ServerSocket(port); + this.serverListenSocket.setSoTimeout(60000); + this.listenerThread = new Thread(this, "EbeanClusterListener"); + + } catch (IOException e) { + String msg = "Error starting cluster socket listener on port " + port; + throw new RuntimeException(msg, e); + } + } + + /** + * Start listening for requests. + */ + public void startListening() throws IOException { + logger.trace("... startListening()"); + 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.error("Error shutting down listener", e); + } + + threadPool.shutdown(); + } + + /** + * 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.execute(request); + + isActive = false; + } + } catch (SocketException e) { + if (doingShutdown) { + logger.info("doingShutdown and accept threw:" + e.getMessage()); + } else { + logger.error("Error while listening", e); + } + } catch (InterruptedIOException e) { + // this will happen when the server is very quiet. + // that is, no requests + logger.debug("Possibly expected due to accept timeout? {}", e.getMessage()); + + } catch (IOException e) { + // log it and continue in the loop... + logger.error("IOException processing cluster message", 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 71bc8b572..f817e2040 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,78 +1,78 @@ -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 fa06064db..209d528b6 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,41 +1,41 @@ -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 aeef8e95b..929a11e17 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,129 +1,129 @@ -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 7f0f6ec76..51a2daf39 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java @@ -1,478 +1,478 @@ -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; - -import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary; - -/** - * 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); - } - if (value instanceof byte[]) { - return ScalarTypeUUIDBinary.convertFromBytes((byte[])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; + +import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary; + +/** + * 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); + } + if (value instanceof byte[]) { + return ScalarTypeUUIDBinary.convertFromBytes((byte[])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 b9aad6820..f261bdee2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java @@ -1,132 +1,132 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.Connection; - -import javax.persistence.PersistenceException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * Base class for find and persist requests. - */ -public abstract class BeanRequest { - - private static final Logger log = LoggerFactory.getLogger(BeanRequest.class); - - /** - * The server processing the request. - */ - protected final SpiEbeanServer ebeanServer; - - protected final String serverName; - - /** - * The transaction this is part of. - */ - protected SpiTransaction transaction; - - protected boolean createdTransaction; - - 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); - createdTransaction = true; - } - } - } - - /** - * Commit this transaction if it was created for this request. - */ - public void commitTransIfRequired() { - if (createdTransaction) { - transaction.commit(); - } - } - - /** - * Rollback the transaction if it was created for this request. - */ - public void rollbackTransIfRequired() { - if (createdTransaction) { - try { - transaction.rollback(); - } catch (PersistenceException e) { - // Just log this and carry on. A previous exception has been - // thrown and if this rollback throws exception it likely means - // that the connection is broken (and the datasource and db will cleanup) - log.error("Error trying to rollack a transaction (after a prior exception thrown)", e); - } - } - } - - /** - * Return the server processing the request. Made available for - * BeanController and BeanFinder. - */ - public EbeanServer getEbeanServer() { - return ebeanServer; - } - - public SpiEbeanServer getServer() { - 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.isLogSql(); - } - - /** - * Return true if SUMMARY information should be logged for this transaction. - */ - public boolean isLogSummary() { - return transaction.isLogSummary(); - } -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.Connection; + +import javax.persistence.PersistenceException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * Base class for find and persist requests. + */ +public abstract class BeanRequest { + + private static final Logger log = LoggerFactory.getLogger(BeanRequest.class); + + /** + * The server processing the request. + */ + protected final SpiEbeanServer ebeanServer; + + protected final String serverName; + + /** + * The transaction this is part of. + */ + protected SpiTransaction transaction; + + protected boolean createdTransaction; + + 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); + createdTransaction = true; + } + } + } + + /** + * Commit this transaction if it was created for this request. + */ + public void commitTransIfRequired() { + if (createdTransaction) { + transaction.commit(); + } + } + + /** + * Rollback the transaction if it was created for this request. + */ + public void rollbackTransIfRequired() { + if (createdTransaction) { + try { + transaction.rollback(); + } catch (PersistenceException e) { + // Just log this and carry on. A previous exception has been + // thrown and if this rollback throws exception it likely means + // that the connection is broken (and the datasource and db will cleanup) + log.error("Error trying to rollack a transaction (after a prior exception thrown)", e); + } + } + } + + /** + * Return the server processing the request. Made available for + * BeanController and BeanFinder. + */ + public EbeanServer getEbeanServer() { + return ebeanServer; + } + + public SpiEbeanServer getServer() { + 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.isLogSql(); + } + + /** + * Return true if SUMMARY information should be logged for this transaction. + */ + public boolean isLogSummary() { + return transaction.isLogSummary(); + } +} 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 2a28160aa..d8c6d6cff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java @@ -1,392 +1,392 @@ -package com.avaje.ebeaninternal.server.core; - -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.List; - -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.Table; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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 = LoggerFactory.getLogger(BootupClasses.class); - - 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> beanQueryAdapterList = new ArrayList>(); - - - private ArrayList> serverConfigStartupList = new ArrayList>(); - private ArrayList serverConfigStartupInstances = new ArrayList(); - - private List persistControllerInstances = new ArrayList(); - private List persistListenerInstances = new ArrayList(); - private List queryAdapterInstances = new ArrayList(); - private List transactionEventListenerInstances = new ArrayList(); - - public BootupClasses() { - } - - public BootupClasses(List> list) { - if (list != null) { - for (Class cls : list) { - isMatch(cls); - } - } - } - - private BootupClasses(BootupClasses parent) { - this.embeddableList.addAll(parent.embeddableList); - this.entityList.addAll(parent.entityList); - this.scalarTypeList.addAll(parent.scalarTypeList); - this.scalarConverterList.addAll(parent.scalarConverterList); - this.compoundTypeList.addAll(parent.compoundTypeList); - this.beanControllerList.addAll(parent.beanControllerList); - this.transactionEventListenerList.addAll(parent.transactionEventListenerList); - this.beanFinderList.addAll(parent.beanFinderList); - this.beanListenerList.addAll(parent.beanListenerList); - this.beanQueryAdapterList.addAll(parent.beanQueryAdapterList); - this.serverConfigStartupList.addAll(parent.serverConfigStartupList); - } - - /** - * Create a copy of this object so that classes can be added to it. - */ - public BootupClasses createCopy() { - return new BootupClasses(this); - } - - /** - * Run any ServerConfigStartup listeners. - */ - public void runServerConfigStartup(ServerConfig serverConfig) { - - for (Class cls : serverConfigStartupList) { - try { - ServerConfigStartup newInstance = (ServerConfigStartup) cls.newInstance(); - newInstance.onStart(serverConfig); - - } catch (Exception e) { - String msg = "Error creating BeanQueryAdapter " + cls; - logger.error(msg, e); - } - } - } - - public void addQueryAdapters(List queryAdapterInstances) { - if (queryAdapterInstances != null) { - for (BeanQueryAdapter a : queryAdapterInstances) { - this.queryAdapterInstances.add(a); - // don't automatically instantiate - this.beanQueryAdapterList.remove(a.getClass()); - } - } - } - - /** - * Add BeanPersistController instances. - */ - public void addPersistControllers(List beanControllerInstances) { - if (beanControllerInstances != null) { - for (BeanPersistController c : beanControllerInstances) { - this.persistControllerInstances.add(c); - // don't automatically instantiate - this.beanControllerList.remove(c.getClass()); - } - } - } - - /** - * Add TransactionEventListeners instances. - */ - public void addTransactionEventListeners(List transactionEventListeners) { - if (transactionEventListeners != null) { - for (TransactionEventListener c : transactionEventListeners) { - this.transactionEventListenerInstances.add(c); - // don't automatically instantiate - this.transactionEventListenerList.remove(c.getClass()); - } - } - } - - public void addPersistListeners(List listenerInstances) { - if (listenerInstances != null) { - for (BeanPersistListener l : listenerInstances) { - this.persistListenerInstances.add(l); - // don't automatically instantiate - this.beanListenerList.remove(l.getClass()); - } - } - } - - public void addServerConfigStartup(List startupInstances) { - if (startupInstances != null) { - for (ServerConfigStartup l : startupInstances) { - this.serverConfigStartupInstances.add(l); - // don't automatically instantiate - this.serverConfigStartupList.remove(l.getClass()); - } - } - } - - public List getBeanQueryAdapters() { - // add class registered BeanQueryAdapter to the - // already created instances - for (Class cls : beanQueryAdapterList) { - try { - BeanQueryAdapter newInstance = (BeanQueryAdapter) cls.newInstance(); - queryAdapterInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanQueryAdapter " + cls; - logger.error(msg, e); - } - } - - return queryAdapterInstances; - } - - public List getBeanPersistListeners() { - // add class registered BeanPersistController to the - // already created instances - for (Class cls : beanListenerList) { - try { - BeanPersistListener newInstance = (BeanPersistListener) cls.newInstance(); - persistListenerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanPersistController " + cls; - logger.error(msg, e); - } - } - - return persistListenerInstances; - } - - public List getBeanPersistControllers() { - // add class registered BeanPersistController to the - // already created instances - for (Class cls : beanControllerList) { - try { - BeanPersistController newInstance = (BeanPersistController) cls.newInstance(); - persistControllerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanPersistController " + cls; - logger.error(msg, e); - } - } - - return persistControllerInstances; - } - - public List getTransactionEventListeners() { - // add class registered TransactionEventListener to the - // already created instances - for (Class cls : transactionEventListenerList) { - try { - TransactionEventListener newInstance = (TransactionEventListener) cls.newInstance(); - transactionEventListenerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating TransactionEventListener " + cls; - logger.error(msg, e); - } - } - - return transactionEventListenerInstances; - } - - /** - * Return the list of Embeddable classes. - */ - public ArrayList> getEmbeddables() { - return embeddableList; - } - - /** - * Return the list of entity classes. - */ - public ArrayList> getEntities() { - return entityList; - } - - /** - * Return the list of ScalarTypes found. - */ - public ArrayList> getScalarTypes() { - return scalarTypeList; - } - - /** - * Return the list of ScalarConverters found. - */ - public ArrayList> getScalarConverters() { - return scalarConverterList; - } - - /** - * Return the list of ScalarConverters found. - */ - public ArrayList> getCompoundTypes() { - return compoundTypeList; - } - - /** - * Return the list of BeanControllers found. - */ - public ArrayList> getBeanControllers() { - return beanControllerList; - } - - /** - * Return the list of TransactionEventListeners found - */ - public ArrayList> getTransactionEventListenerList() { - return transactionEventListenerList; - } - - /** - * Return the list of BeanFinders found. - */ - public ArrayList> getBeanFinders() { - return beanFinderList; - } - - /** - * Return the list of BeanListeners found. - */ - public ArrayList> getBeanListeners() { - return beanListenerList; - } - - public boolean isMatch(Class cls) { - - if (isEmbeddable(cls)) { - embeddableList.add(cls); - - } else if (isEntity(cls)) { - entityList.add(cls); - - } else if (isInterestingInterface(cls)) { - return true; - - } else { - return false; - } - - return true; - } - - /** - * Look for interesting interfaces. - *

- * This includes ScalarType, BeanController, BeanFinder and BeanListener. - *

- */ - private boolean isInterestingInterface(Class cls) { - - boolean interesting = false; - - if (BeanPersistController.class.isAssignableFrom(cls)) { - beanControllerList.add(cls); - interesting = true; - } - - if (TransactionEventListener.class.isAssignableFrom(cls)) { - transactionEventListenerList.add(cls); - interesting = true; - } - - if (ScalarType.class.isAssignableFrom(cls)) { - scalarTypeList.add(cls); - interesting = true; - } - - if (ScalarTypeConverter.class.isAssignableFrom(cls)) { - scalarConverterList.add(cls); - interesting = true; - } - - if (CompoundType.class.isAssignableFrom(cls)) { - compoundTypeList.add(cls); - interesting = true; - } - - if (BeanFinder.class.isAssignableFrom(cls)) { - beanFinderList.add(cls); - interesting = true; - } - - if (BeanPersistListener.class.isAssignableFrom(cls)) { - beanListenerList.add(cls); - interesting = true; - } - - if (BeanQueryAdapter.class.isAssignableFrom(cls)) { - beanQueryAdapterList.add(cls); - interesting = true; - } - - if (ServerConfigStartup.class.isAssignableFrom(cls)){ - serverConfigStartupList.add(cls); - interesting = true; - } - - return interesting; - } - - private boolean isEntity(Class cls) { - - Annotation ann = cls.getAnnotation(Entity.class); - if (ann != null) { - return true; - } - ann = cls.getAnnotation(Table.class); - if (ann != null) { - return true; - } - return false; - } - - private boolean isEmbeddable(Class cls) { - - Annotation ann = cls.getAnnotation(Embeddable.class); - if (ann != null) { - return true; - } - return false; - } -} +package com.avaje.ebeaninternal.server.core; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 = LoggerFactory.getLogger(BootupClasses.class); + + 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> beanQueryAdapterList = new ArrayList>(); + + + private ArrayList> serverConfigStartupList = new ArrayList>(); + private ArrayList serverConfigStartupInstances = new ArrayList(); + + private List persistControllerInstances = new ArrayList(); + private List persistListenerInstances = new ArrayList(); + private List queryAdapterInstances = new ArrayList(); + private List transactionEventListenerInstances = new ArrayList(); + + public BootupClasses() { + } + + public BootupClasses(List> list) { + if (list != null) { + for (Class cls : list) { + isMatch(cls); + } + } + } + + private BootupClasses(BootupClasses parent) { + this.embeddableList.addAll(parent.embeddableList); + this.entityList.addAll(parent.entityList); + this.scalarTypeList.addAll(parent.scalarTypeList); + this.scalarConverterList.addAll(parent.scalarConverterList); + this.compoundTypeList.addAll(parent.compoundTypeList); + this.beanControllerList.addAll(parent.beanControllerList); + this.transactionEventListenerList.addAll(parent.transactionEventListenerList); + this.beanFinderList.addAll(parent.beanFinderList); + this.beanListenerList.addAll(parent.beanListenerList); + this.beanQueryAdapterList.addAll(parent.beanQueryAdapterList); + this.serverConfigStartupList.addAll(parent.serverConfigStartupList); + } + + /** + * Create a copy of this object so that classes can be added to it. + */ + public BootupClasses createCopy() { + return new BootupClasses(this); + } + + /** + * Run any ServerConfigStartup listeners. + */ + public void runServerConfigStartup(ServerConfig serverConfig) { + + for (Class cls : serverConfigStartupList) { + try { + ServerConfigStartup newInstance = (ServerConfigStartup) cls.newInstance(); + newInstance.onStart(serverConfig); + + } catch (Exception e) { + String msg = "Error creating BeanQueryAdapter " + cls; + logger.error(msg, e); + } + } + } + + public void addQueryAdapters(List queryAdapterInstances) { + if (queryAdapterInstances != null) { + for (BeanQueryAdapter a : queryAdapterInstances) { + this.queryAdapterInstances.add(a); + // don't automatically instantiate + this.beanQueryAdapterList.remove(a.getClass()); + } + } + } + + /** + * Add BeanPersistController instances. + */ + public void addPersistControllers(List beanControllerInstances) { + if (beanControllerInstances != null) { + for (BeanPersistController c : beanControllerInstances) { + this.persistControllerInstances.add(c); + // don't automatically instantiate + this.beanControllerList.remove(c.getClass()); + } + } + } + + /** + * Add TransactionEventListeners instances. + */ + public void addTransactionEventListeners(List transactionEventListeners) { + if (transactionEventListeners != null) { + for (TransactionEventListener c : transactionEventListeners) { + this.transactionEventListenerInstances.add(c); + // don't automatically instantiate + this.transactionEventListenerList.remove(c.getClass()); + } + } + } + + public void addPersistListeners(List listenerInstances) { + if (listenerInstances != null) { + for (BeanPersistListener l : listenerInstances) { + this.persistListenerInstances.add(l); + // don't automatically instantiate + this.beanListenerList.remove(l.getClass()); + } + } + } + + public void addServerConfigStartup(List startupInstances) { + if (startupInstances != null) { + for (ServerConfigStartup l : startupInstances) { + this.serverConfigStartupInstances.add(l); + // don't automatically instantiate + this.serverConfigStartupList.remove(l.getClass()); + } + } + } + + public List getBeanQueryAdapters() { + // add class registered BeanQueryAdapter to the + // already created instances + for (Class cls : beanQueryAdapterList) { + try { + BeanQueryAdapter newInstance = (BeanQueryAdapter) cls.newInstance(); + queryAdapterInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanQueryAdapter " + cls; + logger.error(msg, e); + } + } + + return queryAdapterInstances; + } + + public List getBeanPersistListeners() { + // add class registered BeanPersistController to the + // already created instances + for (Class cls : beanListenerList) { + try { + BeanPersistListener newInstance = (BeanPersistListener) cls.newInstance(); + persistListenerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanPersistController " + cls; + logger.error(msg, e); + } + } + + return persistListenerInstances; + } + + public List getBeanPersistControllers() { + // add class registered BeanPersistController to the + // already created instances + for (Class cls : beanControllerList) { + try { + BeanPersistController newInstance = (BeanPersistController) cls.newInstance(); + persistControllerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanPersistController " + cls; + logger.error(msg, e); + } + } + + return persistControllerInstances; + } + + public List getTransactionEventListeners() { + // add class registered TransactionEventListener to the + // already created instances + for (Class cls : transactionEventListenerList) { + try { + TransactionEventListener newInstance = (TransactionEventListener) cls.newInstance(); + transactionEventListenerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating TransactionEventListener " + cls; + logger.error(msg, e); + } + } + + return transactionEventListenerInstances; + } + + /** + * Return the list of Embeddable classes. + */ + public ArrayList> getEmbeddables() { + return embeddableList; + } + + /** + * Return the list of entity classes. + */ + public ArrayList> getEntities() { + return entityList; + } + + /** + * Return the list of ScalarTypes found. + */ + public ArrayList> getScalarTypes() { + return scalarTypeList; + } + + /** + * Return the list of ScalarConverters found. + */ + public ArrayList> getScalarConverters() { + return scalarConverterList; + } + + /** + * Return the list of ScalarConverters found. + */ + public ArrayList> getCompoundTypes() { + return compoundTypeList; + } + + /** + * Return the list of BeanControllers found. + */ + public ArrayList> getBeanControllers() { + return beanControllerList; + } + + /** + * Return the list of TransactionEventListeners found + */ + public ArrayList> getTransactionEventListenerList() { + return transactionEventListenerList; + } + + /** + * Return the list of BeanFinders found. + */ + public ArrayList> getBeanFinders() { + return beanFinderList; + } + + /** + * Return the list of BeanListeners found. + */ + public ArrayList> getBeanListeners() { + return beanListenerList; + } + + public boolean isMatch(Class cls) { + + if (isEmbeddable(cls)) { + embeddableList.add(cls); + + } else if (isEntity(cls)) { + entityList.add(cls); + + } else if (isInterestingInterface(cls)) { + return true; + + } else { + return false; + } + + return true; + } + + /** + * Look for interesting interfaces. + *

+ * This includes ScalarType, BeanController, BeanFinder and BeanListener. + *

+ */ + private boolean isInterestingInterface(Class cls) { + + boolean interesting = false; + + if (BeanPersistController.class.isAssignableFrom(cls)) { + beanControllerList.add(cls); + interesting = true; + } + + if (TransactionEventListener.class.isAssignableFrom(cls)) { + transactionEventListenerList.add(cls); + interesting = true; + } + + if (ScalarType.class.isAssignableFrom(cls)) { + scalarTypeList.add(cls); + interesting = true; + } + + if (ScalarTypeConverter.class.isAssignableFrom(cls)) { + scalarConverterList.add(cls); + interesting = true; + } + + if (CompoundType.class.isAssignableFrom(cls)) { + compoundTypeList.add(cls); + interesting = true; + } + + if (BeanFinder.class.isAssignableFrom(cls)) { + beanFinderList.add(cls); + interesting = true; + } + + if (BeanPersistListener.class.isAssignableFrom(cls)) { + beanListenerList.add(cls); + interesting = true; + } + + if (BeanQueryAdapter.class.isAssignableFrom(cls)) { + beanQueryAdapterList.add(cls); + interesting = true; + } + + if (ServerConfigStartup.class.isAssignableFrom(cls)){ + serverConfigStartupList.add(cls); + interesting = true; + } + + return interesting; + } + + private boolean isEntity(Class cls) { + + Annotation ann = cls.getAnnotation(Entity.class); + if (ann != null) { + return true; + } + ann = cls.getAnnotation(Table.class); + if (ann != null) { + return true; + } + return false; + } + + private boolean isEmbeddable(Class cls) { + + Annotation ann = cls.getAnnotation(Embeddable.class); + if (ann != null) { + return true; + } + return false; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java index 8ae2b34a3..d2600ebc3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -1,166 +1,166 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.dbplatform.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Create a DatabasePlatform from the configuration. - *

- * Will used platform name or use the meta data from the JDBC driver to - * determine the platform automatically. - *

- */ -public class DatabasePlatformFactory { - - private static final Logger logger = LoggerFactory.getLogger(DatabasePlatformFactory.class); - - /** - * Create the appropriate database specific platform. - */ - public DatabasePlatform create(ServerConfig serverConfig) { - - try { - - if (serverConfig.getDatabasePlatformName() != null) { - // choose based on dbName - return byDatabaseName(serverConfig.getDatabasePlatformName()); - - } - if (serverConfig.getDataSourceConfig().isOffline()) { - String m = "You must specify a DatabasePlatformName when you are offline"; - throw new PersistenceException(m); - } - // guess using meta data from driver - return byDataSource(serverConfig.getDataSource()); - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Lookup the platform by name. - */ - private DatabasePlatform byDatabaseName(String dbName) throws SQLException { - - dbName = dbName.toLowerCase(); - if (dbName.equals("postgres") || dbName.equals("postgres9")) { - return new PostgresPlatform(); - } - if (dbName.equals("postgres8") || dbName.equals("postgres83")) { - return new Postgres8Platform(); - } - if (dbName.equals("oracle9")) { - return new Oracle9Platform(); - } - if (dbName.equals("oracle") || dbName.equals("oracle10")) { - return new Oracle10Platform(); - } - if (dbName.equals("sqlserver2005")) { - return new MsSqlServer2005Platform(); - } - if (dbName.equals("sqlserver2000")) { - return new MsSqlServer2000Platform(); - } - if (dbName.equals("sqlanywhere")) { - return new SqlAnywherePlatform(); - } - if (dbName.equals("db2")) { - return new DB2Platform(); - } - if (dbName.equals("mysql")) { - return new MySqlPlatform(); - } - - if (dbName.equals("sqlite")) { - return new SQLitePlatform(); - } - - throw new RuntimeException("database platform " + dbName + " is not known?"); - } - - /** - * Use JDBC DatabaseMetaData to determine the platform. - */ - private DatabasePlatform byDataSource(DataSource dataSource) { - - Connection conn = null; - try { - conn = dataSource.getConnection(); - DatabaseMetaData metaData = conn.getMetaData(); - - return byDatabaseMeta(metaData); - - } catch (SQLException ex) { - throw new PersistenceException(ex); - - } finally { - try { - if (conn != null) { - conn.close(); - } - } catch (SQLException ex) { - logger.error(null, ex); - } - } - } - - /** - * Find the platform by the metaData.getDatabaseProductName(). - */ - private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData) throws SQLException { - - String dbProductName = metaData.getDatabaseProductName(); - dbProductName = dbProductName.toLowerCase(); - - int majorVersion = metaData.getDatabaseMajorVersion(); - - if (dbProductName.contains("oracle")) { - if (majorVersion > 9) { - return new Oracle10Platform(); - } else { - return new Oracle9Platform(); - } - } - else if (dbProductName.contains("microsoft")) { - if (majorVersion > 8) { - return new MsSqlServer2005Platform(); - } else { - return new MsSqlServer2000Platform(); - } - } - else if (dbProductName.contains("mysql")) { - return new MySqlPlatform(); - } - else if (dbProductName.contains("h2")) { - return new H2Platform(); - } - else if (dbProductName.contains("hsql database engine")) { - return new HsqldbPlatform(); - } - else if (dbProductName.contains("postgres")) { - return new PostgresPlatform(); - } - else if (dbProductName.contains("sqlite")) { - return new SQLitePlatform(); - } - else if (dbProductName.contains("db2")) { - return new DB2Platform(); - } - else if (dbProductName.contains("sql anywhere")) { - return new SqlAnywherePlatform(); - } - - // use the standard one - return new DatabasePlatform(); - } -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Create a DatabasePlatform from the configuration. + *

+ * Will used platform name or use the meta data from the JDBC driver to + * determine the platform automatically. + *

+ */ +public class DatabasePlatformFactory { + + private static final Logger logger = LoggerFactory.getLogger(DatabasePlatformFactory.class); + + /** + * Create the appropriate database specific platform. + */ + public DatabasePlatform create(ServerConfig serverConfig) { + + try { + + if (serverConfig.getDatabasePlatformName() != null) { + // choose based on dbName + return byDatabaseName(serverConfig.getDatabasePlatformName()); + + } + if (serverConfig.getDataSourceConfig().isOffline()) { + String m = "You must specify a DatabasePlatformName when you are offline"; + throw new PersistenceException(m); + } + // guess using meta data from driver + return byDataSource(serverConfig.getDataSource()); + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Lookup the platform by name. + */ + private DatabasePlatform byDatabaseName(String dbName) throws SQLException { + + dbName = dbName.toLowerCase(); + if (dbName.equals("postgres") || dbName.equals("postgres9")) { + return new PostgresPlatform(); + } + if (dbName.equals("postgres8") || dbName.equals("postgres83")) { + return new Postgres8Platform(); + } + if (dbName.equals("oracle9")) { + return new Oracle9Platform(); + } + if (dbName.equals("oracle") || dbName.equals("oracle10")) { + return new Oracle10Platform(); + } + if (dbName.equals("sqlserver2005")) { + return new MsSqlServer2005Platform(); + } + if (dbName.equals("sqlserver2000")) { + return new MsSqlServer2000Platform(); + } + if (dbName.equals("sqlanywhere")) { + return new SqlAnywherePlatform(); + } + if (dbName.equals("db2")) { + return new DB2Platform(); + } + if (dbName.equals("mysql")) { + return new MySqlPlatform(); + } + + if (dbName.equals("sqlite")) { + return new SQLitePlatform(); + } + + throw new RuntimeException("database platform " + dbName + " is not known?"); + } + + /** + * Use JDBC DatabaseMetaData to determine the platform. + */ + private DatabasePlatform byDataSource(DataSource dataSource) { + + Connection conn = null; + try { + conn = dataSource.getConnection(); + DatabaseMetaData metaData = conn.getMetaData(); + + return byDatabaseMeta(metaData); + + } catch (SQLException ex) { + throw new PersistenceException(ex); + + } finally { + try { + if (conn != null) { + conn.close(); + } + } catch (SQLException ex) { + logger.error(null, ex); + } + } + } + + /** + * Find the platform by the metaData.getDatabaseProductName(). + */ + private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData) throws SQLException { + + String dbProductName = metaData.getDatabaseProductName(); + dbProductName = dbProductName.toLowerCase(); + + int majorVersion = metaData.getDatabaseMajorVersion(); + + if (dbProductName.contains("oracle")) { + if (majorVersion > 9) { + return new Oracle10Platform(); + } else { + return new Oracle9Platform(); + } + } + else if (dbProductName.contains("microsoft")) { + if (majorVersion > 8) { + return new MsSqlServer2005Platform(); + } else { + return new MsSqlServer2000Platform(); + } + } + else if (dbProductName.contains("mysql")) { + return new MySqlPlatform(); + } + else if (dbProductName.contains("h2")) { + return new H2Platform(); + } + else if (dbProductName.contains("hsql database engine")) { + return new HsqldbPlatform(); + } + else if (dbProductName.contains("postgres")) { + return new PostgresPlatform(); + } + else if (dbProductName.contains("sqlite")) { + return new SQLitePlatform(); + } + else if (dbProductName.contains("db2")) { + return new DB2Platform(); + } + else if (dbProductName.contains("sql anywhere")) { + return new SqlAnywherePlatform(); + } + + // use the standard one + return new DatabasePlatform(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java index 39528b43b..81ba2af0b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java @@ -1,52 +1,52 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.concurrent.TimeUnit; - -import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; -import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool; -import com.avaje.ebeaninternal.server.lib.DaemonThreadPool; - -/** - * The default implementation of the BackgroundExecutor. - */ -public class DefaultBackgroundExecutor implements SpiBackgroundExecutor { - - private final DaemonThreadPool pool; - - private final DaemonScheduleThreadPool schedulePool; - - /** - * Construct the default implementation of BackgroundExecutor. - * - * @param corePoolSize - * the core size of the thread pool. - * @param maximumPoolSize - * the maximum pool size before jobs are queued - * @param keepAliveSecs - * the time in seconds idle threads are keep alive - * @param shutdownWaitSeconds - * the time in seconds allowed for the pool to shutdown nicely. - * After this the pool is forced to shutdown. - */ - public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, int maximumPoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) { - this.pool = new DaemonThreadPool(corePoolSize, maximumPoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix); - this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-"); - } - - /** - * Execute a Runnable using a background thread. - */ - public void execute(Runnable r) { - pool.execute(r); - } - - public void executePeriodically(Runnable r, long delay, TimeUnit unit) { - schedulePool.scheduleWithFixedDelay(r, delay, delay, unit); - } - - public void shutdown() { - pool.shutdown(); - schedulePool.shutdown(); - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.util.concurrent.TimeUnit; + +import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; +import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool; +import com.avaje.ebeaninternal.server.lib.DaemonThreadPool; + +/** + * The default implementation of the BackgroundExecutor. + */ +public class DefaultBackgroundExecutor implements SpiBackgroundExecutor { + + private final DaemonThreadPool pool; + + private final DaemonScheduleThreadPool schedulePool; + + /** + * Construct the default implementation of BackgroundExecutor. + * + * @param corePoolSize + * the core size of the thread pool. + * @param maximumPoolSize + * the maximum pool size before jobs are queued + * @param keepAliveSecs + * the time in seconds idle threads are keep alive + * @param shutdownWaitSeconds + * the time in seconds allowed for the pool to shutdown nicely. + * After this the pool is forced to shutdown. + */ + public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, int maximumPoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) { + this.pool = new DaemonThreadPool(corePoolSize, maximumPoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix); + this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-"); + } + + /** + * Execute a Runnable using a background thread. + */ + public void execute(Runnable r) { + pool.execute(r); + } + + public void executePeriodically(Runnable r, long delay, TimeUnit unit) { + schedulePool.scheduleWithFixedDelay(r, delay, delay, unit); + } + + public void shutdown() { + pool.shutdown(); + schedulePool.shutdown(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 8e2cfc385..4f9231540 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -1,442 +1,442 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.ArrayList; -import java.util.List; - -import javax.persistence.EntityNotFoundException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.ExpressionList; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.LoadBeanBuffer; -import com.avaje.ebeaninternal.api.LoadBeanRequest; -import com.avaje.ebeaninternal.api.LoadManyRequest; -import com.avaje.ebeaninternal.api.LoadManyBuffer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; - -/** - * Helper to handle lazy loading and refreshing of beans. - */ -public class DefaultBeanLoader { - - private static final Logger logger = LoggerFactory.getLogger(DefaultBeanLoader.class); - - private final DefaultServer server; - - protected DefaultBeanLoader(DefaultServer server) { - this.server = server; - } - - /** - * Return a batch size that might be less than the requestedBatchSize. - *

- * This means we can have large and variable requestedBatchSizes. - *

- *

- * We want to restrict the number of different batch sizes as we want to - * re-use the query plan cache and get DB statement re-use. - *

- */ - private int getBatchSize(int batchSize) { - - if (batchSize == 1) { - // there is only one bean/collection to load - return 1; - } - if (batchSize <= 5) { - // anything less than 5 becomes 5 - return 5; - } - if (batchSize <= 10) { - return 10; - } - if (batchSize <= 20) { - return 20; - } - if (batchSize <= 50) { - return 50; - } - if (batchSize <= 100) { - return 100; - } - return batchSize; - } - - public void refreshMany(EntityBean parentBean, String propertyName) { - refreshMany(parentBean, propertyName, null); - } - - public void loadMany(LoadManyRequest loadRequest) { - - List> batch = loadRequest.getBatch(); - - int batchSize = getBatchSize(batch.size()); - - LoadManyBuffer ctx = loadRequest.getLoadContext(); - BeanPropertyAssocMany many = ctx.getBeanProperty(); - - PersistenceContext pc = ctx.getPersistenceContext(); - - ArrayList idList = new ArrayList(batchSize); - - for (int i = 0; i < batch.size(); i++) { - BeanCollection bc = batch.get(i); - EntityBean ownerBean = bc.getOwnerBean(); - Object id = many.getParentId(ownerBean); - idList.add(id); - } - int extraIds = batchSize - batch.size(); - if (extraIds > 0) { - Object firstId = idList.get(0); - for (int i = 0; i < extraIds; i++) { - idList.add(firstId); - } - } - - BeanDescriptor desc = ctx.getBeanDescriptor(); - - SpiQuery query = (SpiQuery) server.createQuery(many.getTargetType()); - String orderBy = many.getLazyFetchOrderBy(); - if (orderBy != null) { - query.orderBy(orderBy); - } - - String extraWhere = many.getExtraWhere(); - if (extraWhere != null) { - // replace special ${ta} placeholder with the base table alias - // which is always t0 and add the extra where clause - String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0"); - query.where().raw(ew); - } - - query.setLazyLoadForParents(idList, many); - many.addWhereParentIdIn(query, idList); - - query.setPersistenceContext(pc); - - String mode = loadRequest.isLazy() ? "+lazy" : "+query"; - query.setLoadDescription(mode, loadRequest.getDescription()); - - if (loadRequest.isLazy()) { - // cascade the batch size (if set) for further lazy loading - query.setLazyLoadBatchSize(loadRequest.getBatchSize()); - } - - // potentially changes the joins and selected properties - ctx.configureQuery(query); - - if (loadRequest.isOnlyIds()) { - // override to just select the Id values - query.select(many.getTargetIdProperty()); - } - - server.findList(query, loadRequest.getTransaction()); - - // check for BeanCollection's that where never processed - // in the +query or +lazy load due to no rows (predicates) - for (int i = 0; i < batch.size(); i++) { - BeanCollection bc = batch.get(i); - if (bc.checkEmptyLazyLoad()) { - if (logger.isDebugEnabled()) { - logger.debug("BeanCollection after load was empty. Owner:" + batch.get(i).getOwnerBean()); - } - } else if (loadRequest.isLoadCache()) { - Object parentId = desc.getId(bc.getOwnerBean()); - desc.cacheManyPropPut(many, bc, parentId); - } - } - - // log the query (for testing secondary queries) - loadRequest.logSecondaryQuery(query); - } - - public void loadMany(BeanCollection bc, boolean onlyIds) { - - EntityBean parentBean = bc.getOwnerBean(); - String propertyName = bc.getPropertyName(); - - loadManyInternal(parentBean, propertyName, null, false, null, onlyIds); - } - - public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) { - loadManyInternal(parentBean, propertyName, t, true, null, false); - } - - private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, - ObjectGraphNode node, boolean onlyIds) { - - EntityBeanIntercept ebi = parentBean._ebean_getIntercept(); - PersistenceContext pc = ebi.getPersistenceContext(); - - BeanDescriptor parentDesc = server.getBeanDescriptor(parentBean.getClass()); - BeanPropertyAssocMany many = (BeanPropertyAssocMany) parentDesc.getBeanProperty(propertyName); - - BeanCollection beanCollection = null; - ExpressionList filterMany = null; - - Object currentValue = many.getValue(parentBean); - if (currentValue instanceof BeanCollection) { - beanCollection = (BeanCollection) currentValue; - filterMany = beanCollection.getFilterMany(); - } - - Object parentId = parentDesc.getId(parentBean); - - if (pc == null) { - pc = new DefaultPersistenceContext(); - pc.put(parentId, parentBean); - } - - boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching(); - if (useManyIdCache) { - Boolean readOnly = null; - if (ebi.isReadOnly()) { - readOnly = Boolean.TRUE; - } - if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) { - return; - } - } - - SpiQuery query = (SpiQuery) server.createQuery(parentDesc.getBeanType()); - - if (refresh) { - // populate a new collection - BeanCollection emptyCollection = many.createEmpty(parentBean); - many.setValue(parentBean, emptyCollection); - query.setLoadDescription("+refresh", null); - } else { - query.setLoadDescription("+lazy", null); - } - - if (node != null) { - // so we can hook back to the root query - query.setParentNode(node); - } - - String idProperty = parentDesc.getIdBinder().getIdProperty(); - query.select(idProperty); - - if (onlyIds) { - query.fetch(many.getName(), many.getTargetIdProperty()); - } else { - query.fetch(many.getName()); - } - if (filterMany != null) { - query.setFilterMany(many.getName(), filterMany); - } - - query.where().idEq(parentId); - query.setUseCache(false); - query.setMode(Mode.LAZYLOAD_MANY); - query.setLazyLoadManyPath(many.getName()); - query.setPersistenceContext(pc); - - if (ebi.isReadOnly()) { - query.setReadOnly(true); - } - - server.findUnique(query, t); - - if (beanCollection != null) { - if (beanCollection.checkEmptyLazyLoad()) { - if (logger.isDebugEnabled()) { - logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean()); - } - } else if (useManyIdCache) { - parentDesc.cacheManyPropPut(many, beanCollection, parentId); - } - } - } - - - /** - * Load a batch of beans for +query or +lazy loading. - */ - public void loadBean(LoadBeanRequest loadRequest) { - - List batch = loadRequest.getBatch(); - - if (batch.isEmpty()) { - throw new RuntimeException("Nothing in batch?"); - } - - int batchSize = getBatchSize(batch.size()); - - LoadBeanBuffer ctx = loadRequest.getLoadContext(); - BeanDescriptor desc = ctx.getBeanDescriptor(); - - Class beanType = desc.getBeanType(); - - EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]); - ArrayList idList = new ArrayList(batchSize); - - for (int i = 0; i < batch.size(); i++) { - EntityBeanIntercept ebi = batch.get(i); - EntityBean bean = ebi.getOwner(); - Object id = desc.getId(bean); - idList.add(id); - } - - if (idList.isEmpty()) { - // everything was loaded from cache - return; - } - - int extraIds = batchSize - batch.size(); - if (extraIds > 0) { - // for performance make up the Id's to the batch size - // so we get the same query (for Ebean and the db) - Object firstId = idList.get(0); - for (int i = 0; i < extraIds; i++) { - // just add the first Id again - idList.add(firstId); - } - } - - PersistenceContext persistenceContext = ctx.getPersistenceContext(); - - SpiQuery query = (SpiQuery) server.createQuery(beanType); - - query.setMode(Mode.LAZYLOAD_BEAN); - query.setPersistenceContext(persistenceContext); - - String mode = loadRequest.isLazy() ? "+lazy" : "+query"; - query.setLoadDescription(mode, loadRequest.getDescription()); - - if (loadRequest.isLazy()) { - // cascade the batch size (if set) for further lazy loading - query.setLazyLoadBatchSize(loadRequest.getBatchSize()); - } - - ctx.configureQuery(query, loadRequest.getLazyLoadProperty()); - - // make sure the query doesn't use the cache - // query.setUseCache(false); - if (idList.size() == 1) { - query.where().idEq(idList.get(0)); - } else { - query.where().idIn(idList); - } - - List list = server.findList(query, loadRequest.getTransaction()); - - if (loadRequest.isLoadCache()) { - for (int i = 0; i < list.size(); i++) { - desc.cacheBeanPutData((EntityBean) list.get(i)); - } - } - - for (int i = 0; i < ebis.length; i++) { - // Check if the underlying row in DB was deleted. Mark this bean as 'failed' if - // necessary but allow processing to continue until it is accessed by client code - ebis[i].checkLazyLoadFailure(); - } - - // log the query (for testing secondary queries) - loadRequest.logSecondaryQuery(query); - } - - public void refresh(EntityBean bean) { - refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1); - } - - public void loadBean(EntityBeanIntercept ebi) { - refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1); - } - - private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) { - - EntityBeanIntercept ebi = bean._ebean_getIntercept(); - PersistenceContext pc = ebi.getPersistenceContext(); - if (Mode.REFRESH_BEAN == mode) { - // need a new PersistenceContext for REFRESH - pc = null; - } - - BeanDescriptor desc = server.getBeanDescriptor(bean.getClass()); - if (EntityType.EMBEDDED == desc.getEntityType()) { - // lazy loading on an embedded bean property - EntityBean embeddedOwner = (EntityBean) ebi.getEmbeddedOwner(); - int ownerIndex = ebi.getEmbeddedOwnerIndex(); - - refreshBeanInternal(embeddedOwner, mode, ownerIndex); - } - - Object id = desc.getId(bean); - - if (pc == null) { - // a reference with no existing persistenceContext - pc = new DefaultPersistenceContext(); - pc.put(id, bean); - ebi.setPersistenceContext(pc); - } - - if (embeddedOwnerIndex == -1) { - if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) { - // lazy loading and the bean cache is active - if (desc.cacheBeanLoad(bean, ebi, id)) { - return; - } - } - if (desc.lazyLoadMany(ebi)) { - return; - } - } - - SpiQuery query = (SpiQuery) server.createQuery(desc.getBeanType()); - query.setLazyLoadProperty(ebi.getLazyLoadProperty()); - - if (embeddedOwnerIndex > -1) { - String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex); - query.select("id," + embeddedBeanPropertyName); - } - - // don't collect autoFetch usage profiling information - // as we just copy the data out of these fetched beans - // and put the data into the original bean - query.setUsageProfiling(false); - query.setPersistenceContext(pc); - - query.setMode(mode); - query.setId(id); - - if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) { - // make sure the query doesn't use the cache - query.setUseCache(false); - } - - if (ebi.isReadOnly()) { - query.setReadOnly(true); - } - - if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) { - // explicitly state to load all properties on REFRESH. - // Lobs default to fetch lazy so this forces lobs to be - // included in a 'refresh' query - query.select("*"); - } - - Object dbBean = query.findUnique(); - if (dbBean == null) { - String msg = "Bean not found during lazy load or refresh." + " id[" + id + "] type[" + desc.getBeanType() + "]"; - throw new EntityNotFoundException(msg); - } - - desc.resetManyProperties(dbBean); - - } -} +package com.avaje.ebeaninternal.server.core; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityNotFoundException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.ExpressionList; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.LoadBeanBuffer; +import com.avaje.ebeaninternal.api.LoadBeanRequest; +import com.avaje.ebeaninternal.api.LoadManyRequest; +import com.avaje.ebeaninternal.api.LoadManyBuffer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; + +/** + * Helper to handle lazy loading and refreshing of beans. + */ +public class DefaultBeanLoader { + + private static final Logger logger = LoggerFactory.getLogger(DefaultBeanLoader.class); + + private final DefaultServer server; + + protected DefaultBeanLoader(DefaultServer server) { + this.server = server; + } + + /** + * Return a batch size that might be less than the requestedBatchSize. + *

+ * This means we can have large and variable requestedBatchSizes. + *

+ *

+ * We want to restrict the number of different batch sizes as we want to + * re-use the query plan cache and get DB statement re-use. + *

+ */ + private int getBatchSize(int batchSize) { + + if (batchSize == 1) { + // there is only one bean/collection to load + return 1; + } + if (batchSize <= 5) { + // anything less than 5 becomes 5 + return 5; + } + if (batchSize <= 10) { + return 10; + } + if (batchSize <= 20) { + return 20; + } + if (batchSize <= 50) { + return 50; + } + if (batchSize <= 100) { + return 100; + } + return batchSize; + } + + public void refreshMany(EntityBean parentBean, String propertyName) { + refreshMany(parentBean, propertyName, null); + } + + public void loadMany(LoadManyRequest loadRequest) { + + List> batch = loadRequest.getBatch(); + + int batchSize = getBatchSize(batch.size()); + + LoadManyBuffer ctx = loadRequest.getLoadContext(); + BeanPropertyAssocMany many = ctx.getBeanProperty(); + + PersistenceContext pc = ctx.getPersistenceContext(); + + ArrayList idList = new ArrayList(batchSize); + + for (int i = 0; i < batch.size(); i++) { + BeanCollection bc = batch.get(i); + EntityBean ownerBean = bc.getOwnerBean(); + Object id = many.getParentId(ownerBean); + idList.add(id); + } + int extraIds = batchSize - batch.size(); + if (extraIds > 0) { + Object firstId = idList.get(0); + for (int i = 0; i < extraIds; i++) { + idList.add(firstId); + } + } + + BeanDescriptor desc = ctx.getBeanDescriptor(); + + SpiQuery query = (SpiQuery) server.createQuery(many.getTargetType()); + String orderBy = many.getLazyFetchOrderBy(); + if (orderBy != null) { + query.orderBy(orderBy); + } + + String extraWhere = many.getExtraWhere(); + if (extraWhere != null) { + // replace special ${ta} placeholder with the base table alias + // which is always t0 and add the extra where clause + String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0"); + query.where().raw(ew); + } + + query.setLazyLoadForParents(idList, many); + many.addWhereParentIdIn(query, idList); + + query.setPersistenceContext(pc); + + String mode = loadRequest.isLazy() ? "+lazy" : "+query"; + query.setLoadDescription(mode, loadRequest.getDescription()); + + if (loadRequest.isLazy()) { + // cascade the batch size (if set) for further lazy loading + query.setLazyLoadBatchSize(loadRequest.getBatchSize()); + } + + // potentially changes the joins and selected properties + ctx.configureQuery(query); + + if (loadRequest.isOnlyIds()) { + // override to just select the Id values + query.select(many.getTargetIdProperty()); + } + + server.findList(query, loadRequest.getTransaction()); + + // check for BeanCollection's that where never processed + // in the +query or +lazy load due to no rows (predicates) + for (int i = 0; i < batch.size(); i++) { + BeanCollection bc = batch.get(i); + if (bc.checkEmptyLazyLoad()) { + if (logger.isDebugEnabled()) { + logger.debug("BeanCollection after load was empty. Owner:" + batch.get(i).getOwnerBean()); + } + } else if (loadRequest.isLoadCache()) { + Object parentId = desc.getId(bc.getOwnerBean()); + desc.cacheManyPropPut(many, bc, parentId); + } + } + + // log the query (for testing secondary queries) + loadRequest.logSecondaryQuery(query); + } + + public void loadMany(BeanCollection bc, boolean onlyIds) { + + EntityBean parentBean = bc.getOwnerBean(); + String propertyName = bc.getPropertyName(); + + loadManyInternal(parentBean, propertyName, null, false, null, onlyIds); + } + + public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) { + loadManyInternal(parentBean, propertyName, t, true, null, false); + } + + private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, + ObjectGraphNode node, boolean onlyIds) { + + EntityBeanIntercept ebi = parentBean._ebean_getIntercept(); + PersistenceContext pc = ebi.getPersistenceContext(); + + BeanDescriptor parentDesc = server.getBeanDescriptor(parentBean.getClass()); + BeanPropertyAssocMany many = (BeanPropertyAssocMany) parentDesc.getBeanProperty(propertyName); + + BeanCollection beanCollection = null; + ExpressionList filterMany = null; + + Object currentValue = many.getValue(parentBean); + if (currentValue instanceof BeanCollection) { + beanCollection = (BeanCollection) currentValue; + filterMany = beanCollection.getFilterMany(); + } + + Object parentId = parentDesc.getId(parentBean); + + if (pc == null) { + pc = new DefaultPersistenceContext(); + pc.put(parentId, parentBean); + } + + boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching(); + if (useManyIdCache) { + Boolean readOnly = null; + if (ebi.isReadOnly()) { + readOnly = Boolean.TRUE; + } + if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) { + return; + } + } + + SpiQuery query = (SpiQuery) server.createQuery(parentDesc.getBeanType()); + + if (refresh) { + // populate a new collection + BeanCollection emptyCollection = many.createEmpty(parentBean); + many.setValue(parentBean, emptyCollection); + query.setLoadDescription("+refresh", null); + } else { + query.setLoadDescription("+lazy", null); + } + + if (node != null) { + // so we can hook back to the root query + query.setParentNode(node); + } + + String idProperty = parentDesc.getIdBinder().getIdProperty(); + query.select(idProperty); + + if (onlyIds) { + query.fetch(many.getName(), many.getTargetIdProperty()); + } else { + query.fetch(many.getName()); + } + if (filterMany != null) { + query.setFilterMany(many.getName(), filterMany); + } + + query.where().idEq(parentId); + query.setUseCache(false); + query.setMode(Mode.LAZYLOAD_MANY); + query.setLazyLoadManyPath(many.getName()); + query.setPersistenceContext(pc); + + if (ebi.isReadOnly()) { + query.setReadOnly(true); + } + + server.findUnique(query, t); + + if (beanCollection != null) { + if (beanCollection.checkEmptyLazyLoad()) { + if (logger.isDebugEnabled()) { + logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean()); + } + } else if (useManyIdCache) { + parentDesc.cacheManyPropPut(many, beanCollection, parentId); + } + } + } + + + /** + * Load a batch of beans for +query or +lazy loading. + */ + public void loadBean(LoadBeanRequest loadRequest) { + + List batch = loadRequest.getBatch(); + + if (batch.isEmpty()) { + throw new RuntimeException("Nothing in batch?"); + } + + int batchSize = getBatchSize(batch.size()); + + LoadBeanBuffer ctx = loadRequest.getLoadContext(); + BeanDescriptor desc = ctx.getBeanDescriptor(); + + Class beanType = desc.getBeanType(); + + EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]); + ArrayList idList = new ArrayList(batchSize); + + for (int i = 0; i < batch.size(); i++) { + EntityBeanIntercept ebi = batch.get(i); + EntityBean bean = ebi.getOwner(); + Object id = desc.getId(bean); + idList.add(id); + } + + if (idList.isEmpty()) { + // everything was loaded from cache + return; + } + + int extraIds = batchSize - batch.size(); + if (extraIds > 0) { + // for performance make up the Id's to the batch size + // so we get the same query (for Ebean and the db) + Object firstId = idList.get(0); + for (int i = 0; i < extraIds; i++) { + // just add the first Id again + idList.add(firstId); + } + } + + PersistenceContext persistenceContext = ctx.getPersistenceContext(); + + SpiQuery query = (SpiQuery) server.createQuery(beanType); + + query.setMode(Mode.LAZYLOAD_BEAN); + query.setPersistenceContext(persistenceContext); + + String mode = loadRequest.isLazy() ? "+lazy" : "+query"; + query.setLoadDescription(mode, loadRequest.getDescription()); + + if (loadRequest.isLazy()) { + // cascade the batch size (if set) for further lazy loading + query.setLazyLoadBatchSize(loadRequest.getBatchSize()); + } + + ctx.configureQuery(query, loadRequest.getLazyLoadProperty()); + + // make sure the query doesn't use the cache + // query.setUseCache(false); + if (idList.size() == 1) { + query.where().idEq(idList.get(0)); + } else { + query.where().idIn(idList); + } + + List list = server.findList(query, loadRequest.getTransaction()); + + if (loadRequest.isLoadCache()) { + for (int i = 0; i < list.size(); i++) { + desc.cacheBeanPutData((EntityBean) list.get(i)); + } + } + + for (int i = 0; i < ebis.length; i++) { + // Check if the underlying row in DB was deleted. Mark this bean as 'failed' if + // necessary but allow processing to continue until it is accessed by client code + ebis[i].checkLazyLoadFailure(); + } + + // log the query (for testing secondary queries) + loadRequest.logSecondaryQuery(query); + } + + public void refresh(EntityBean bean) { + refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1); + } + + public void loadBean(EntityBeanIntercept ebi) { + refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1); + } + + private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) { + + EntityBeanIntercept ebi = bean._ebean_getIntercept(); + PersistenceContext pc = ebi.getPersistenceContext(); + if (Mode.REFRESH_BEAN == mode) { + // need a new PersistenceContext for REFRESH + pc = null; + } + + BeanDescriptor desc = server.getBeanDescriptor(bean.getClass()); + if (EntityType.EMBEDDED == desc.getEntityType()) { + // lazy loading on an embedded bean property + EntityBean embeddedOwner = (EntityBean) ebi.getEmbeddedOwner(); + int ownerIndex = ebi.getEmbeddedOwnerIndex(); + + refreshBeanInternal(embeddedOwner, mode, ownerIndex); + } + + Object id = desc.getId(bean); + + if (pc == null) { + // a reference with no existing persistenceContext + pc = new DefaultPersistenceContext(); + pc.put(id, bean); + ebi.setPersistenceContext(pc); + } + + if (embeddedOwnerIndex == -1) { + if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) { + // lazy loading and the bean cache is active + if (desc.cacheBeanLoad(bean, ebi, id)) { + return; + } + } + if (desc.lazyLoadMany(ebi)) { + return; + } + } + + SpiQuery query = (SpiQuery) server.createQuery(desc.getBeanType()); + query.setLazyLoadProperty(ebi.getLazyLoadProperty()); + + if (embeddedOwnerIndex > -1) { + String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex); + query.select("id," + embeddedBeanPropertyName); + } + + // don't collect autoFetch usage profiling information + // as we just copy the data out of these fetched beans + // and put the data into the original bean + query.setUsageProfiling(false); + query.setPersistenceContext(pc); + + query.setMode(mode); + query.setId(id); + + if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) { + // make sure the query doesn't use the cache + query.setUseCache(false); + } + + if (ebi.isReadOnly()) { + query.setReadOnly(true); + } + + if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) { + // explicitly state to load all properties on REFRESH. + // Lobs default to fetch lazy so this forces lobs to be + // included in a 'refresh' query + query.select("*"); + } + + Object dbBean = query.findUnique(); + if (dbBean == null) { + String msg = "Bean not found during lazy load or refresh." + " id[" + id + "] type[" + desc.getBeanType() + "]"; + throw new EntityNotFoundException(msg); + } + + desc.resetManyProperties(dbBean); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java index 5c13112a0..85090eb8e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java @@ -1,123 +1,123 @@ -package com.avaje.ebeaninternal.server.core; - -import java.io.Serializable; -import java.sql.CallableStatement; -import java.sql.SQLException; - -import com.avaje.ebean.CallableSql; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.BindParams.Param; -import com.avaje.ebeaninternal.api.SpiCallableSql; -import com.avaje.ebeaninternal.api.TransactionEventTable; - - -public class DefaultCallableSql implements Serializable, SpiCallableSql { - - private static final long serialVersionUID = 8984272253185424701L; - - private transient final EbeanServer server; - - /** - * The callable sql. - */ - private String sql; - - /** - * To display in the transaction log to help identify the procedure. - */ - private String label; - - private int timeout; - - /** - * Holds the table modification information. On commit this information is - * used to manage the cache etc. - */ - private final TransactionEventTable transactionEvent = new TransactionEventTable(); - - private final BindParams bindParameters = new BindParams(); - - /** - * Create with callable sql. - */ - public DefaultCallableSql(EbeanServer server, String sql) { - this.server = server; - this.sql = sql; - } - - public void execute() { - server.execute(this, null); - } - - public String getLabel() { - return label; - } - - public CallableSql setLabel(String label) { - this.label = label; - return this; - } - - public int getTimeout() { - return timeout; - } - - public String getSql() { - return sql; - } - - public CallableSql setTimeout(int secs) { - this.timeout = secs; - return this; - } - - public CallableSql setSql(String sql) { - this.sql = sql; - return this; - } - - public CallableSql bind(int position, Object value) { - bindParameters.setParameter(position, value); - return this; - } - - public CallableSql setParameter(int position, Object value) { - bindParameters.setParameter(position, value); - return this; - } - - public CallableSql registerOut(int position, int type) { - bindParameters.registerOut(position, type); - return this; - } - - public Object getObject(int position) { - Param p = bindParameters.getParameter(position); - return p.getOutValue(); - } - - public boolean executeOverride(CallableStatement cstmt) throws SQLException { - return false; - } - - public CallableSql addModification(String tableName, boolean inserts, boolean updates, boolean deletes) { - - transactionEvent.add(tableName, inserts, updates, deletes); - return this; - } - - /** - * Return the TransactionEvent which holds the table modification - * information for this CallableSql. This information is merged into the - * transaction after the transaction is commited. - */ - public TransactionEventTable getTransactionEventTable() { - return transactionEvent; - } - - public BindParams getBindParams() { - return bindParameters; - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.io.Serializable; +import java.sql.CallableStatement; +import java.sql.SQLException; + +import com.avaje.ebean.CallableSql; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.BindParams.Param; +import com.avaje.ebeaninternal.api.SpiCallableSql; +import com.avaje.ebeaninternal.api.TransactionEventTable; + + +public class DefaultCallableSql implements Serializable, SpiCallableSql { + + private static final long serialVersionUID = 8984272253185424701L; + + private transient final EbeanServer server; + + /** + * The callable sql. + */ + private String sql; + + /** + * To display in the transaction log to help identify the procedure. + */ + private String label; + + private int timeout; + + /** + * Holds the table modification information. On commit this information is + * used to manage the cache etc. + */ + private final TransactionEventTable transactionEvent = new TransactionEventTable(); + + private final BindParams bindParameters = new BindParams(); + + /** + * Create with callable sql. + */ + public DefaultCallableSql(EbeanServer server, String sql) { + this.server = server; + this.sql = sql; + } + + public void execute() { + server.execute(this, null); + } + + public String getLabel() { + return label; + } + + public CallableSql setLabel(String label) { + this.label = label; + return this; + } + + public int getTimeout() { + return timeout; + } + + public String getSql() { + return sql; + } + + public CallableSql setTimeout(int secs) { + this.timeout = secs; + return this; + } + + public CallableSql setSql(String sql) { + this.sql = sql; + return this; + } + + public CallableSql bind(int position, Object value) { + bindParameters.setParameter(position, value); + return this; + } + + public CallableSql setParameter(int position, Object value) { + bindParameters.setParameter(position, value); + return this; + } + + public CallableSql registerOut(int position, int type) { + bindParameters.registerOut(position, type); + return this; + } + + public Object getObject(int position) { + Param p = bindParameters.getParameter(position); + return p.getOutValue(); + } + + public boolean executeOverride(CallableStatement cstmt) throws SQLException { + return false; + } + + public CallableSql addModification(String tableName, boolean inserts, boolean updates, boolean deletes) { + + transactionEvent.add(tableName, inserts, updates, deletes); + return this; + } + + /** + * Return the TransactionEvent which holds the table modification + * information for this CallableSql. This information is merged into the + * transaction after the transaction is commited. + */ + public TransactionEventTable getTransactionEventTable() { + return transactionEvent; + } + + public BindParams getBindParams() { + return bindParameters; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java index b188e9358..dd8b96edb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java @@ -1,241 +1,241 @@ -package com.avaje.ebeaninternal.server.core; - -import java.io.Serializable; - -import com.avaje.ebean.Ebean; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Update; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiSqlUpdate; - -/** - * A SQL Update Delete or Insert statement that can be executed. For the times - * when you want to use Sql DML rather than a ORM bean approach. Refer to the - * Ebean execute() method. - *

- * There is also {@link Update} which is similar except should use logical bean and - * property names rather than physical table and column names. - *

- *

- * SqlUpdate is designed for general DML sql and CallableSql is - * designed for use with stored procedures. - *

- * - *
- * // String sql = "update f_topic set post_count = :count where id = :topicId";
- * 
- * SqlUpdate update = new SqlUpdate(sql);
- * update.setParameter("count", 1);
- * update.setParameter("topicId", 50);
- * 
- * int modifiedCount = Ebean.execute(update);
- * 
- * - *

- * Note that when the SqlUpdate is executed via Ebean.execute() the sql is - * parsed to determine if it is an update, delete or insert. In addition the - * table modified is deduced. If isAutoTableMod() is true, then this - * is then added to the TransactionEvent and cache invalidation etc is - * maintained. This means you don't need to use the Ebean.externalModification() - * method as this has already been done. - *

- *

- * You can sql.setAutoTableMod(false); to stop the automatic table modification - *

- *

- * EXAMPLE: Using JDBC batching with SqlUpdate - *

- *
- * 
- * String data = "This is a simple test of the batch processing"
- * 		+ " mode and the transaction execute batch method";
- * 
- * String[] da = data.split(" ");
- * 
- * String sql = "insert into junk (word) values (?)";
- * 
- * SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
- * 
- * Transaction t = Ebean.beginTransaction();
- * t.setBatchMode(true);
- * t.setBatchSize(3);
- * try {
- * 	for (int i = 0; i < da.length; i++) {
- * 
- * 		sqlUpdate.setParameter(1, da[i]);
- * 		sqlUpdate.execute();
- * 	}
- * 
- * 	// NB: commit implicitly flushes the batch 
- * 	Ebean.commitTransaction();
- * 
- * } finally {
- * 	Ebean.endTransaction();
- * }
- * 
- * @see com.avaje.ebean.CallableSql - * @see com.avaje.ebean.Ebean#execute(SqlUpdate) - */ -public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate { - - private static final long serialVersionUID = -6493829438421253102L; - - private transient final EbeanServer server; - - /** - * The parameters used to bind to the sql. - */ - private final BindParams bindParams; - - /** - * The sql update or delete statement. - */ - private final String sql; - - /** - * The actual sql with named parameters converted. - */ - private String generatedSql; - - /** - * Some descriptive text that can be put into the transaction log. - */ - private String label = ""; - - /** - * The statement execution timeout. - */ - private int timeout; - - /** - * Automatically detect the table being modified by this sql. This will - * register this information so that eBean invalidates cached objects if - * required. - */ - private boolean isAutoTableMod = true; - - /** - * Helper to add positioned parameters in order. - */ - private int addPos; - - /** - * Create with server sql and bindParams object. - *

- * Useful if you are building the sql and binding parameters at the - * same time. - *

- */ - public DefaultSqlUpdate(EbeanServer server, String sql, BindParams bindParams) { - this.server = server; - this.sql = sql; - this.bindParams = bindParams; - } - - /** - * Create with a specific server. This means you can use the - * SqlUpdate.execute() method. - */ - public DefaultSqlUpdate(EbeanServer server, String sql) { - this(server, sql, new BindParams()); - } - - /** - * Create with some sql. - */ - public DefaultSqlUpdate(String sql) { - this(null, sql, new BindParams()); - } - - public int execute() { - if (server != null) { - return server.execute(this); - } else { - // Hopefully this doesn't catch anyone out... - return Ebean.execute(this); - } - } - - public boolean isAutoTableMod() { - return isAutoTableMod; - } - - public SqlUpdate setAutoTableMod(boolean isAutoTableMod) { - this.isAutoTableMod = isAutoTableMod; - return this; - } - - public String getLabel() { - return label; - } - - public SqlUpdate setLabel(String label) { - this.label = label; - return this; - } - - public String getGeneratedSql() { - return generatedSql; - } - - @Override - public void setGeneratedSql(String generatedSql) { - this.generatedSql = generatedSql; - } - - public String getSql() { - return sql; - } - - public int getTimeout() { - return timeout; - } - - public SqlUpdate setTimeout(int secs) { - this.timeout = secs; - return this; - } - - public SqlUpdate addParameter(Object value) { - return setParameter(++addPos, value); - } - - public SqlUpdate setParameter(int position, Object value) { - bindParams.setParameter(position, value); - return this; - } - - public SqlUpdate setNull(int position, int jdbcType) { - bindParams.setNullParameter(position, jdbcType); - return this; - } - - public SqlUpdate setNullParameter(int position, int jdbcType) { - bindParams.setNullParameter(position, jdbcType); - return this; - } - - public SqlUpdate setParameter(String name, Object param) { - bindParams.setParameter(name, param); - return this; - } - - public SqlUpdate setNull(String name, int jdbcType) { - bindParams.setNullParameter(name, jdbcType); - return this; - } - - public SqlUpdate setNullParameter(String name, int jdbcType) { - bindParams.setNullParameter(name, jdbcType); - return this; - } - - /** - * Return the bind parameters. - */ - public BindParams getBindParams() { - return bindParams; - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.io.Serializable; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Update; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiSqlUpdate; + +/** + * A SQL Update Delete or Insert statement that can be executed. For the times + * when you want to use Sql DML rather than a ORM bean approach. Refer to the + * Ebean execute() method. + *

+ * There is also {@link Update} which is similar except should use logical bean and + * property names rather than physical table and column names. + *

+ *

+ * SqlUpdate is designed for general DML sql and CallableSql is + * designed for use with stored procedures. + *

+ * + *
+ * // String sql = "update f_topic set post_count = :count where id = :topicId";
+ * 
+ * SqlUpdate update = new SqlUpdate(sql);
+ * update.setParameter("count", 1);
+ * update.setParameter("topicId", 50);
+ * 
+ * int modifiedCount = Ebean.execute(update);
+ * 
+ * + *

+ * Note that when the SqlUpdate is executed via Ebean.execute() the sql is + * parsed to determine if it is an update, delete or insert. In addition the + * table modified is deduced. If isAutoTableMod() is true, then this + * is then added to the TransactionEvent and cache invalidation etc is + * maintained. This means you don't need to use the Ebean.externalModification() + * method as this has already been done. + *

+ *

+ * You can sql.setAutoTableMod(false); to stop the automatic table modification + *

+ *

+ * EXAMPLE: Using JDBC batching with SqlUpdate + *

+ *
+ * 
+ * String data = "This is a simple test of the batch processing"
+ * 		+ " mode and the transaction execute batch method";
+ * 
+ * String[] da = data.split(" ");
+ * 
+ * String sql = "insert into junk (word) values (?)";
+ * 
+ * SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
+ * 
+ * Transaction t = Ebean.beginTransaction();
+ * t.setBatchMode(true);
+ * t.setBatchSize(3);
+ * try {
+ * 	for (int i = 0; i < da.length; i++) {
+ * 
+ * 		sqlUpdate.setParameter(1, da[i]);
+ * 		sqlUpdate.execute();
+ * 	}
+ * 
+ * 	// NB: commit implicitly flushes the batch 
+ * 	Ebean.commitTransaction();
+ * 
+ * } finally {
+ * 	Ebean.endTransaction();
+ * }
+ * 
+ * @see com.avaje.ebean.CallableSql + * @see com.avaje.ebean.Ebean#execute(SqlUpdate) + */ +public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate { + + private static final long serialVersionUID = -6493829438421253102L; + + private transient final EbeanServer server; + + /** + * The parameters used to bind to the sql. + */ + private final BindParams bindParams; + + /** + * The sql update or delete statement. + */ + private final String sql; + + /** + * The actual sql with named parameters converted. + */ + private String generatedSql; + + /** + * Some descriptive text that can be put into the transaction log. + */ + private String label = ""; + + /** + * The statement execution timeout. + */ + private int timeout; + + /** + * Automatically detect the table being modified by this sql. This will + * register this information so that eBean invalidates cached objects if + * required. + */ + private boolean isAutoTableMod = true; + + /** + * Helper to add positioned parameters in order. + */ + private int addPos; + + /** + * Create with server sql and bindParams object. + *

+ * Useful if you are building the sql and binding parameters at the + * same time. + *

+ */ + public DefaultSqlUpdate(EbeanServer server, String sql, BindParams bindParams) { + this.server = server; + this.sql = sql; + this.bindParams = bindParams; + } + + /** + * Create with a specific server. This means you can use the + * SqlUpdate.execute() method. + */ + public DefaultSqlUpdate(EbeanServer server, String sql) { + this(server, sql, new BindParams()); + } + + /** + * Create with some sql. + */ + public DefaultSqlUpdate(String sql) { + this(null, sql, new BindParams()); + } + + public int execute() { + if (server != null) { + return server.execute(this); + } else { + // Hopefully this doesn't catch anyone out... + return Ebean.execute(this); + } + } + + public boolean isAutoTableMod() { + return isAutoTableMod; + } + + public SqlUpdate setAutoTableMod(boolean isAutoTableMod) { + this.isAutoTableMod = isAutoTableMod; + return this; + } + + public String getLabel() { + return label; + } + + public SqlUpdate setLabel(String label) { + this.label = label; + return this; + } + + public String getGeneratedSql() { + return generatedSql; + } + + @Override + public void setGeneratedSql(String generatedSql) { + this.generatedSql = generatedSql; + } + + public String getSql() { + return sql; + } + + public int getTimeout() { + return timeout; + } + + public SqlUpdate setTimeout(int secs) { + this.timeout = secs; + return this; + } + + public SqlUpdate addParameter(Object value) { + return setParameter(++addPos, value); + } + + public SqlUpdate setParameter(int position, Object value) { + bindParams.setParameter(position, value); + return this; + } + + public SqlUpdate setNull(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public SqlUpdate setNullParameter(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public SqlUpdate setParameter(String name, Object param) { + bindParams.setParameter(name, param); + return this; + } + + public SqlUpdate setNull(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + public SqlUpdate setNullParameter(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + /** + * Return the bind parameters. + */ + public BindParams getBindParams() { + return bindParams; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java index e775b87be..58c2884ad 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java @@ -1,148 +1,148 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.LinkedHashMap; -import java.util.Map; - -import com.avaje.ebean.ValuePair; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.util.ValueUtil; - -/** - * Helper to perform a diff given two beans of the same type. - *

- * This intentionally does not include any OneToMany or ManyToMany properties. - *

- */ -public class DiffHelp { - - - /** - * Return a map of the differences between a and b. - *

- * A and B must be of the same type. B can be null, in which case the 'dirty - * values' of a is returned. - *

- *

- * This intentionally does not include as OneToMany or ManyToMany properties. - *

- */ - public Map diff(Object a, Object b, BeanDescriptor desc) { - - if (a instanceof EntityBean == false) { - throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+a); - } - - if (b != null) { - if (b instanceof EntityBean == false) { - throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+b); - } - if (!a.getClass().isAssignableFrom(b.getClass())) { - throw new IllegalArgumentException("Second bean not assignable to the first bean?"); - } - } - - if (b == null) { - return ((EntityBean) a)._ebean_getIntercept().getDirtyValues(); - } - - Map map = new LinkedHashMap(); - diff(null, map, (EntityBean)a, (EntityBean)b, desc); - return map; - } - - public void diff(String prefix, Map map, EntityBean first, EntityBean sec, BeanDescriptor desc) { - - // check the simple properties - BeanProperty[] base = desc.propertiesBaseScalar(); - for (int i = 0; i < base.length; i++) { - Object aval = base[i].getValue(first); - Object bval = base[i].getValue(sec); - if (!ValueUtil.areEqual(aval, bval)) { - String propName = (prefix == null) ? base[i].getName() : prefix + base[i].getName(); - map.put(propName, new ValuePair(aval, bval)); - } - } - - diffAssocOne(prefix, first, sec, desc, map); - diffEmbedded(prefix, first, sec, desc, map); - } - - /** - * Check the Embedded bean properties for differences. - *

- * If ANY of the properties are different then the whole Embedded bean is - * determined to be different as is added to the map. - *

- */ - private void diffEmbedded(String prefix, EntityBean a, EntityBean b, BeanDescriptor desc, Map map) { - - BeanPropertyAssocOne[] emb = desc.propertiesEmbedded(); - - for (int i = 0; i < emb.length; i++) { - EntityBean aval = (EntityBean)emb[i].getValue(a); - EntityBean bval = (EntityBean)emb[i].getValue(b); - - if (!isBothNull(aval, bval)) { - String propName = (prefix == null) ? emb[i].getName() : prefix + emb[i].getName(); - if (isDiffNull(aval, bval)) { - // one of the embedded beans is null - map.put(propName, new ValuePair(aval, bval)); - - } else { - // recursively diff into the embedded bean - BeanDescriptor embDesc = emb[i].getTargetDescriptor(); - diff(emb[i].getName()+".", map, aval, bval, embDesc); - } - } - } - } - - /** - * If the properties are different by null OR if the id value is different, - * then add the Assoc One bean to the map. - */ - private void diffAssocOne(String prefix, EntityBean a, EntityBean b, BeanDescriptor desc, Map map) { - - BeanPropertyAssocOne[] ones = desc.propertiesOne(); - - for (int i = 0; i < ones.length; i++) { - Object aval = ones[i].getValue(a); - Object bval = ones[i].getValue(b); - - if (!isBothNull(aval, bval)) { - String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName(); - if (isDiffNull(aval, bval)) { - // one of them is/was null - map.put(propName, new ValuePair(aval, bval)); - - } else { - // check to see if the Id properties - // are different - BeanDescriptor oneDesc = ones[i].getTargetDescriptor(); - Object aOneId = oneDesc.getId((EntityBean)aval); - Object bOneId = oneDesc.getId((EntityBean)bval); - - if (!ValueUtil.areEqual(aOneId, bOneId)) { - // the ids are different - map.put(propName, new ValuePair(aval, bval)); - } - } - } - } - } - - private boolean isBothNull(Object aval, Object bval) { - return aval == null && bval == null; - } - - private boolean isDiffNull(Object aval, Object bval) { - if (aval == null) { - return bval != null; - } else { - return bval == null; - } - } -} +package com.avaje.ebeaninternal.server.core; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.avaje.ebean.ValuePair; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.util.ValueUtil; + +/** + * Helper to perform a diff given two beans of the same type. + *

+ * This intentionally does not include any OneToMany or ManyToMany properties. + *

+ */ +public class DiffHelp { + + + /** + * Return a map of the differences between a and b. + *

+ * A and B must be of the same type. B can be null, in which case the 'dirty + * values' of a is returned. + *

+ *

+ * This intentionally does not include as OneToMany or ManyToMany properties. + *

+ */ + public Map diff(Object a, Object b, BeanDescriptor desc) { + + if (a instanceof EntityBean == false) { + throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+a); + } + + if (b != null) { + if (b instanceof EntityBean == false) { + throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+b); + } + if (!a.getClass().isAssignableFrom(b.getClass())) { + throw new IllegalArgumentException("Second bean not assignable to the first bean?"); + } + } + + if (b == null) { + return ((EntityBean) a)._ebean_getIntercept().getDirtyValues(); + } + + Map map = new LinkedHashMap(); + diff(null, map, (EntityBean)a, (EntityBean)b, desc); + return map; + } + + public void diff(String prefix, Map map, EntityBean first, EntityBean sec, BeanDescriptor desc) { + + // check the simple properties + BeanProperty[] base = desc.propertiesBaseScalar(); + for (int i = 0; i < base.length; i++) { + Object aval = base[i].getValue(first); + Object bval = base[i].getValue(sec); + if (!ValueUtil.areEqual(aval, bval)) { + String propName = (prefix == null) ? base[i].getName() : prefix + base[i].getName(); + map.put(propName, new ValuePair(aval, bval)); + } + } + + diffAssocOne(prefix, first, sec, desc, map); + diffEmbedded(prefix, first, sec, desc, map); + } + + /** + * Check the Embedded bean properties for differences. + *

+ * If ANY of the properties are different then the whole Embedded bean is + * determined to be different as is added to the map. + *

+ */ + private void diffEmbedded(String prefix, EntityBean a, EntityBean b, BeanDescriptor desc, Map map) { + + BeanPropertyAssocOne[] emb = desc.propertiesEmbedded(); + + for (int i = 0; i < emb.length; i++) { + EntityBean aval = (EntityBean)emb[i].getValue(a); + EntityBean bval = (EntityBean)emb[i].getValue(b); + + if (!isBothNull(aval, bval)) { + String propName = (prefix == null) ? emb[i].getName() : prefix + emb[i].getName(); + if (isDiffNull(aval, bval)) { + // one of the embedded beans is null + map.put(propName, new ValuePair(aval, bval)); + + } else { + // recursively diff into the embedded bean + BeanDescriptor embDesc = emb[i].getTargetDescriptor(); + diff(emb[i].getName()+".", map, aval, bval, embDesc); + } + } + } + } + + /** + * If the properties are different by null OR if the id value is different, + * then add the Assoc One bean to the map. + */ + private void diffAssocOne(String prefix, EntityBean a, EntityBean b, BeanDescriptor desc, Map map) { + + BeanPropertyAssocOne[] ones = desc.propertiesOne(); + + for (int i = 0; i < ones.length; i++) { + Object aval = ones[i].getValue(a); + Object bval = ones[i].getValue(b); + + if (!isBothNull(aval, bval)) { + String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName(); + if (isDiffNull(aval, bval)) { + // one of them is/was null + map.put(propName, new ValuePair(aval, bval)); + + } else { + // check to see if the Id properties + // are different + BeanDescriptor oneDesc = ones[i].getTargetDescriptor(); + Object aOneId = oneDesc.getId((EntityBean)aval); + Object bOneId = oneDesc.getId((EntityBean)bval); + + if (!ValueUtil.areEqual(aOneId, bOneId)) { + // the ids are different + map.put(propName, new ValuePair(aval, bval)); + } + } + } + } + } + + private boolean isBothNull(Object aval, Object bval) { + return aval == null && bval == null; + } + + private boolean isDiffNull(Object aval, Object bval) { + if (aval == null) { + return bval != null; + } else { + return bval == null; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index a02bbe763..4dda623be 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -1,268 +1,268 @@ -package com.avaje.ebeaninternal.server.core; - -import javax.sql.DataSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.ExpressionFactory; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.ExternalTransactionManager; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory; -import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.DeployOrmXml; -import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties; -import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit; -import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil; -import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory; -import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool; -import com.avaje.ebeaninternal.server.persist.Binder; -import com.avaje.ebeaninternal.server.persist.DefaultPersister; -import com.avaje.ebeaninternal.server.query.CQueryEngine; -import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine; -import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; -import com.avaje.ebeaninternal.server.resource.ResourceManager; -import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; -import com.avaje.ebeaninternal.server.text.json.DJsonContext; -import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; -import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; -import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; -import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager; -import com.avaje.ebeaninternal.server.transaction.TransactionManager; -import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; -import com.avaje.ebeaninternal.server.type.DefaultTypeManager; -import com.avaje.ebeaninternal.server.type.TypeManager; -import com.fasterxml.jackson.core.JsonFactory; - -/** - * Used to extend the ServerConfig with additional objects used to configure and - * construct an EbeanServer. - * - * @author rbygrave - */ -public class InternalConfiguration { - - private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class); - - private final ServerConfig serverConfig; - - private final BootupClasses bootupClasses; - - private final DeployInherit deployInherit; - - private final ResourceManager resourceManager; - - private final DeployOrmXml deployOrmXml; - - private final TypeManager typeManager; - - private final Binder binder; - - private final DeployCreateProperties deployCreateProperties; - - private final DeployUtil deployUtil; - - private final BeanDescriptorManager beanDescriptorManager; - - private final TransactionManager transactionManager; - - private final TransactionScopeManager transactionScopeManager; - - private final CQueryEngine cQueryEngine; - - private final ClusterManager clusterManager; - - private final ServerCacheManager cacheManager; - - private final ExpressionFactory expressionFactory; - - private final SpiBackgroundExecutor backgroundExecutor; - - private final PstmtBatch pstmtBatch; - - private final XmlConfig xmlConfig; - - private final JsonFactory jsonFactory; - - public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, - ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, - ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) { - - this.jsonFactory = serverConfig.getJsonFactory(); - this.xmlConfig = xmlConfig; - this.pstmtBatch = pstmtBatch; - this.clusterManager = clusterManager; - this.backgroundExecutor = backgroundExecutor; - this.cacheManager = cacheManager; - this.serverConfig = serverConfig; - this.bootupClasses = bootupClasses; - this.expressionFactory = new DefaultExpressionFactory(); - - this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); - this.binder = new Binder(typeManager); - - this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig); - this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource()); - this.deployInherit = new DeployInherit(bootupClasses); - - this.deployCreateProperties = new DeployCreateProperties(typeManager); - this.deployUtil = new DeployUtil(typeManager, serverConfig); - - this.beanDescriptorManager = new BeanDescriptorManager(this); - beanDescriptorManager.deploy(); - - this.transactionManager = createTransactionManager(); - - this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder); - - ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager(); - if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) { - externalTransactionManager = new JtaTransactionManager(); - } - if (externalTransactionManager != null) { - externalTransactionManager.setTransactionManager(transactionManager); - this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager); - logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]"); - } else { - this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager); - } - - } - - /** - * Create the TransactionManager taking into account autoCommit mode. - */ - private TransactionManager createTransactionManager() { - - if (isAutoCommitMode()) { - return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); - } - - return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); - } - - /** - * Return true if autoCommit mode is on. - */ - private boolean isAutoCommitMode() { - if (serverConfig.isAutoCommitMode()) { - // explicitly set - return true; - } - DataSource dataSource = serverConfig.getDataSource(); - if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) { - // We know the DataSourcePool is using autoCommit - return true; - } - return false; - } - - public JsonContext createJsonContext(SpiEbeanServer server) { - - return new DJsonContext(server, jsonFactory); - } - - public XmlConfig getXmlConfig() { - return xmlConfig; - } - - public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) { - return AutoFetchManagerFactory.create(server, serverConfig, resourceManager); - } - - public RelationalQueryEngine createRelationalQueryEngine() { - return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue()); - } - - public OrmQueryEngine createOrmQueryEngine() { - return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine); - } - - public Persister createPersister(SpiEbeanServer server) { - return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch); - } - - public PstmtBatch getPstmtBatch() { - return pstmtBatch; - } - - public ServerCacheManager getCacheManager() { - return cacheManager; - } - - public BootupClasses getBootupClasses() { - return bootupClasses; - } - - public DatabasePlatform getDatabasePlatform() { - return serverConfig.getDatabasePlatform(); - } - - public ServerConfig getServerConfig() { - return serverConfig; - } - - public ExpressionFactory getExpressionFactory() { - return expressionFactory; - } - - public TypeManager getTypeManager() { - return typeManager; - } - - public Binder getBinder() { - return binder; - } - - public BeanDescriptorManager getBeanDescriptorManager() { - return beanDescriptorManager; - } - - public DeployInherit getDeployInherit() { - return deployInherit; - } - - public ResourceManager getResourceManager() { - return resourceManager; - } - - public DeployOrmXml getDeployOrmXml() { - return deployOrmXml; - } - - public DeployCreateProperties getDeployCreateProperties() { - return deployCreateProperties; - } - - public DeployUtil getDeployUtil() { - return deployUtil; - } - - public TransactionManager getTransactionManager() { - return transactionManager; - } - - public TransactionScopeManager getTransactionScopeManager() { - return transactionScopeManager; - } - - public CQueryEngine getCQueryEngine() { - return cQueryEngine; - } - - public ClusterManager getClusterManager() { - return clusterManager; - } - - public SpiBackgroundExecutor getBackgroundExecutor() { - return backgroundExecutor; - } - -} +package com.avaje.ebeaninternal.server.core; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.ExternalTransactionManager; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.DeployOrmXml; +import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties; +import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit; +import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil; +import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory; +import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.persist.DefaultPersister; +import com.avaje.ebeaninternal.server.query.CQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine; +import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; +import com.avaje.ebeaninternal.server.resource.ResourceManager; +import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; +import com.avaje.ebeaninternal.server.text.json.DJsonContext; +import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; +import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager; +import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; +import com.avaje.ebeaninternal.server.type.DefaultTypeManager; +import com.avaje.ebeaninternal.server.type.TypeManager; +import com.fasterxml.jackson.core.JsonFactory; + +/** + * Used to extend the ServerConfig with additional objects used to configure and + * construct an EbeanServer. + * + * @author rbygrave + */ +public class InternalConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class); + + private final ServerConfig serverConfig; + + private final BootupClasses bootupClasses; + + private final DeployInherit deployInherit; + + private final ResourceManager resourceManager; + + private final DeployOrmXml deployOrmXml; + + private final TypeManager typeManager; + + private final Binder binder; + + private final DeployCreateProperties deployCreateProperties; + + private final DeployUtil deployUtil; + + private final BeanDescriptorManager beanDescriptorManager; + + private final TransactionManager transactionManager; + + private final TransactionScopeManager transactionScopeManager; + + private final CQueryEngine cQueryEngine; + + private final ClusterManager clusterManager; + + private final ServerCacheManager cacheManager; + + private final ExpressionFactory expressionFactory; + + private final SpiBackgroundExecutor backgroundExecutor; + + private final PstmtBatch pstmtBatch; + + private final XmlConfig xmlConfig; + + private final JsonFactory jsonFactory; + + public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, + ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, + ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) { + + this.jsonFactory = serverConfig.getJsonFactory(); + this.xmlConfig = xmlConfig; + this.pstmtBatch = pstmtBatch; + this.clusterManager = clusterManager; + this.backgroundExecutor = backgroundExecutor; + this.cacheManager = cacheManager; + this.serverConfig = serverConfig; + this.bootupClasses = bootupClasses; + this.expressionFactory = new DefaultExpressionFactory(); + + this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); + this.binder = new Binder(typeManager); + + this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig); + this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource()); + this.deployInherit = new DeployInherit(bootupClasses); + + this.deployCreateProperties = new DeployCreateProperties(typeManager); + this.deployUtil = new DeployUtil(typeManager, serverConfig); + + this.beanDescriptorManager = new BeanDescriptorManager(this); + beanDescriptorManager.deploy(); + + this.transactionManager = createTransactionManager(); + + this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder); + + ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager(); + if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) { + externalTransactionManager = new JtaTransactionManager(); + } + if (externalTransactionManager != null) { + externalTransactionManager.setTransactionManager(transactionManager); + this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager); + logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]"); + } else { + this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager); + } + + } + + /** + * Create the TransactionManager taking into account autoCommit mode. + */ + private TransactionManager createTransactionManager() { + + if (isAutoCommitMode()) { + return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses()); + } + + /** + * Return true if autoCommit mode is on. + */ + private boolean isAutoCommitMode() { + if (serverConfig.isAutoCommitMode()) { + // explicitly set + return true; + } + DataSource dataSource = serverConfig.getDataSource(); + if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) { + // We know the DataSourcePool is using autoCommit + return true; + } + return false; + } + + public JsonContext createJsonContext(SpiEbeanServer server) { + + return new DJsonContext(server, jsonFactory); + } + + public XmlConfig getXmlConfig() { + return xmlConfig; + } + + public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) { + return AutoFetchManagerFactory.create(server, serverConfig, resourceManager); + } + + public RelationalQueryEngine createRelationalQueryEngine() { + return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue()); + } + + public OrmQueryEngine createOrmQueryEngine() { + return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine); + } + + public Persister createPersister(SpiEbeanServer server) { + return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch); + } + + public PstmtBatch getPstmtBatch() { + return pstmtBatch; + } + + public ServerCacheManager getCacheManager() { + return cacheManager; + } + + public BootupClasses getBootupClasses() { + return bootupClasses; + } + + public DatabasePlatform getDatabasePlatform() { + return serverConfig.getDatabasePlatform(); + } + + public ServerConfig getServerConfig() { + return serverConfig; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + public TypeManager getTypeManager() { + return typeManager; + } + + public Binder getBinder() { + return binder; + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public DeployInherit getDeployInherit() { + return deployInherit; + } + + public ResourceManager getResourceManager() { + return resourceManager; + } + + public DeployOrmXml getDeployOrmXml() { + return deployOrmXml; + } + + public DeployCreateProperties getDeployCreateProperties() { + return deployCreateProperties; + } + + public DeployUtil getDeployUtil() { + return deployUtil; + } + + public TransactionManager getTransactionManager() { + return transactionManager; + } + + public TransactionScopeManager getTransactionScopeManager() { + return transactionScopeManager; + } + + public CQueryEngine getCQueryEngine() { + return cQueryEngine; + } + + public ClusterManager getClusterManager() { + return clusterManager; + } + + public SpiBackgroundExecutor getBackgroundExecutor() { + return backgroundExecutor; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java b/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java index f21efc5a9..3bd662510 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java @@ -1,44 +1,44 @@ -package com.avaje.ebeaninternal.server.core; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -/** - * Helper to lookup a DataSource from JNDI. - */ -public class JndiDataSourceLookup { - - private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/"; - - public JndiDataSourceLookup() { - } - - /** - * Return the DataSource by JNDI lookup. - *

- * If name is null the 'default' dataSource is returned. - *

- */ - public DataSource lookup(String jndiName) { - - try { - - if (!jndiName.startsWith("java:")){ - jndiName = DEFAULT_PREFIX + jndiName; - } - - Context ctx = new InitialContext(); - DataSource ds = (DataSource) ctx.lookup(jndiName); - if (ds == null) { - throw new PersistenceException("JNDI DataSource [" + jndiName + "] not found?"); - } - return ds; - - } catch (NamingException ex) { - throw new PersistenceException(ex); - } - } -} +package com.avaje.ebeaninternal.server.core; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +/** + * Helper to lookup a DataSource from JNDI. + */ +public class JndiDataSourceLookup { + + private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/"; + + public JndiDataSourceLookup() { + } + + /** + * Return the DataSource by JNDI lookup. + *

+ * If name is null the 'default' dataSource is returned. + *

+ */ + public DataSource lookup(String jndiName) { + + try { + + if (!jndiName.startsWith("java:")){ + jndiName = DEFAULT_PREFIX + jndiName; + } + + Context ctx = new InitialContext(); + DataSource ds = (DataSource) ctx.lookup(jndiName); + if (ds == null) { + throw new PersistenceException("JNDI DataSource [" + jndiName + "] not found?"); + } + return ds; + + } catch (NamingException ex) { + throw new PersistenceException(ex); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Message.java b/src/main/java/com/avaje/ebeaninternal/server/core/Message.java index 83c7a5f20..b129864b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Message.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Message.java @@ -1,64 +1,64 @@ -package com.avaje.ebeaninternal.server.core; - -import java.text.MessageFormat; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -/** - * Utility object used for internationalising log messages. - */ -public class Message { - - private static final String bundle = "com.avaje.ebeaninternal.api.message"; - - /** - * Return a message that has a single argument. - */ - public static String msg(String key, Object arg) { - Object[] args = new Object[1]; - args[0] = arg; - return MessageFormat.format(getPattern(key), args); - } - - /** - * Return a message that has a two arguments. - */ - public static String msg(String key, Object arg, Object arg2) { - Object[] args = new Object[2]; - args[0] = arg; - args[1] = arg2; - return MessageFormat.format(getPattern(key), args); - } - - public static String msg(String key, Object arg, Object arg2, Object arg3) { - Object[] args = new Object[3]; - args[0] = arg; - args[1] = arg2; - args[2] = arg3; - return MessageFormat.format(getPattern(key), args); - } - - /** - * Return a message that has an array of arguments. - */ - public static String msg(String key, Object[] args) { - return MessageFormat.format(getPattern(key), args); - } - - /** - * Return a message that has a no arguments. - */ - public static String msg(String key) { - return MessageFormat.format(getPattern(key), new Object[0]); - } - - private static String getPattern(String key) { - try { - ResourceBundle myResources = ResourceBundle.getBundle(bundle); - return myResources.getString(key); - } catch (MissingResourceException e) { - return "MissingResource " + bundle + ":" + key; - } - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Utility object used for internationalising log messages. + */ +public class Message { + + private static final String bundle = "com.avaje.ebeaninternal.api.message"; + + /** + * Return a message that has a single argument. + */ + public static String msg(String key, Object arg) { + Object[] args = new Object[1]; + args[0] = arg; + return MessageFormat.format(getPattern(key), args); + } + + /** + * Return a message that has a two arguments. + */ + public static String msg(String key, Object arg, Object arg2) { + Object[] args = new Object[2]; + args[0] = arg; + args[1] = arg2; + return MessageFormat.format(getPattern(key), args); + } + + public static String msg(String key, Object arg, Object arg2, Object arg3) { + Object[] args = new Object[3]; + args[0] = arg; + args[1] = arg2; + args[2] = arg3; + return MessageFormat.format(getPattern(key), args); + } + + /** + * Return a message that has an array of arguments. + */ + public static String msg(String key, Object[] args) { + return MessageFormat.format(getPattern(key), args); + } + + /** + * Return a message that has a no arguments. + */ + public static String msg(String key) { + return MessageFormat.format(getPattern(key), new Object[0]); + } + + private static String getPattern(String key) { + try { + ResourceBundle myResources = ResourceBundle.getBundle(bundle); + return myResources.getString(key); + } catch (MissingResourceException e) { + return "MissingResource " + bundle + ":" + key; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java b/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java index 59f602aaa..d003cdb88 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java @@ -1,22 +1,22 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher; - -/** - * Matcher used for searching for Embeddable, Entity and ScalarTypes in the - * class path. - */ -public class OnBootupClassSearchMatcher implements ClassPathSearchMatcher { - - BootupClasses classes = new BootupClasses(); - - public boolean isMatch(Class cls) { - - return classes.isMatch(cls); - } - - public BootupClasses getOnBootupClasses() { - return classes; - } - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher; + +/** + * Matcher used for searching for Embeddable, Entity and ScalarTypes in the + * class path. + */ +public class OnBootupClassSearchMatcher implements ClassPathSearchMatcher { + + BootupClasses classes = new BootupClasses(); + + public boolean isMatch(Class cls) { + + return classes.isMatch(cls); + } + + public BootupClasses getOnBootupClasses() { + return classes; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java index f7230bb81..aa6f3b0d2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java @@ -1,38 +1,38 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebeaninternal.api.BeanIdList; - -/** - * The Object Relational query execution API. - */ -public interface OrmQueryEngine { - - /** - * Execute the 'find by id' query returning a single bean. - */ - public T findId(OrmQueryRequest request); - - /** - * Execute the findList, findSet, findMap query returning an appropriate BeanCollection. - */ - public BeanCollection findMany(OrmQueryRequest request); - - /** - * Execute the query using a QueryIterator. - */ - public QueryIterator findIterate(OrmQueryRequest request); - - /** - * Execute the row count query. - */ - public int findRowCount(OrmQueryRequest request); - - /** - * Execute the find id's query. - */ - public BeanIdList findIds(OrmQueryRequest request); - - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebeaninternal.api.BeanIdList; + +/** + * The Object Relational query execution API. + */ +public interface OrmQueryEngine { + + /** + * Execute the 'find by id' query returning a single bean. + */ + public T findId(OrmQueryRequest request); + + /** + * Execute the findList, findSet, findMap query returning an appropriate BeanCollection. + */ + public BeanCollection findMany(OrmQueryRequest request); + + /** + * Execute the query using a QueryIterator. + */ + public QueryIterator findIterate(OrmQueryRequest request); + + /** + * Execute the row count query. + */ + public int findRowCount(OrmQueryRequest request); + + /** + * Execute the find id's query. + */ + public BeanIdList findIds(OrmQueryRequest request); + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index 57e3a7d7a..992198295 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -1,415 +1,415 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.*; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebean.event.BeanQueryRequest; -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.HashQuery; -import com.avaje.ebeaninternal.api.HashQueryPlan; -import com.avaje.ebeaninternal.api.LoadContext; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Type; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DeployParser; -import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap; -import com.avaje.ebeaninternal.server.loadcontext.DLoadContext; -import com.avaje.ebeaninternal.server.query.CQueryPlan; -import com.avaje.ebeaninternal.server.query.CancelableQuery; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; - -/** - * Wraps the objects involved in executing a Query. - */ -public final class OrmQueryRequest extends BeanRequest implements BeanQueryRequest, SpiOrmQueryRequest { - - private final BeanDescriptor beanDescriptor; - - private final OrmQueryEngine queryEngine; - - private final SpiQuery query; - - private final BeanFinder finder; - - private final Boolean readOnly; - - private final RawSql rawSql; - - private LoadContext loadContext; - - private PersistenceContext persistenceContext; - - private HashQuery cacheKey; - - private HashQueryPlan queryPlanHash; - - /** - * Create the InternalQueryRequest. - */ - public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery query, BeanDescriptor desc, SpiTransaction t) { - - super(server, t); - - this.beanDescriptor = desc; - this.rawSql = query.getRawSql(); - this.finder = beanDescriptor.getBeanFinder(); - this.queryEngine = queryEngine; - this.query = query; - this.readOnly = query.isReadOnly(); - } - - - /** - * Return the database platform like clause. - */ - @Override - public String getDBLikeClause() { - return ebeanServer.getDatabasePlatform().getLikeClause(); - } - - public void executeSecondaryQueries() { - loadContext.executeSecondaryQueries(this); - } - - /** - * For use with QueryIterator and secondary queries this returns the minimum - * batch size that should be loaded before executing the secondary queries. - *

- * If -1 is returned then NO secondary queries are registered and simple - * iteration is fine. - *

- */ - public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) { - return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch); - } - - /** - * Return the Normal, sharedInstance, ReadOnly state of this query. - */ - public Boolean isReadOnly() { - return readOnly; - } - - /** - * Return the BeanDescriptor for the associated bean. - */ - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - /** - * Return the graph context for this query. - */ - public LoadContext getGraphContext() { - return loadContext; - } - - /** - * Calculate the query plan hash AFTER any potential AutoFetch tuning. - */ - public void calculateQueryPlanHash() { - this.queryPlanHash = query.queryPlanHash(this); - } - - public boolean isRawSql() { - return rawSql != null; - } - - public DeployParser createDeployParser() { - if (rawSql != null) { - return new DeployPropertyParserMap(rawSql.getColumnMapping().getMapping()); - } else { - return beanDescriptor.createDeployPropertyParser(); - } - } - - /** - * Return true if this is a query using generated sql. If false this query - * will use raw sql (Entity bean based on raw sql select). - */ - public boolean isSqlSelect() { - return query.isSqlSelect() && query.getRawSql() == null; - } - - /** - * Return the PersistenceContext used for this request. - */ - public PersistenceContext getPersistenceContext() { - return persistenceContext; - } - - /** - * This will create a local (readOnly) transaction if no current transaction - * exists. - *

- * A transaction may have been passed in explicitly or currently be active in - * the thread local. If not, then a readOnly transaction is created to execute - * this query. - *

- */ - @Override - public void initTransIfRequired() { - // first check if the query requires its own transaction - if (transaction == null) { - // maybe a current one - transaction = ebeanServer.getCurrentServerTransaction(); - if (transaction == null) { - // create an implicit transaction to execute this query - transaction = ebeanServer.createQueryTransaction(); - createdTransaction = true; - } - } - // initialise the persistenceContext and loadContext - this.persistenceContext = getPersistenceContext(query, transaction); - this.loadContext = new DLoadContext(this); - this.loadContext.registerSecondaryQueries(query); - } - - /** - * For iterate queries reset the persistenceContext and loadContext. - */ - public void flushPersistenceContextOnIterate() { - persistenceContext = new DefaultPersistenceContext(); - loadContext.resetPersistenceContext(persistenceContext); - } - - /** - * Get the TransactionContext either explicitly set on the query or - * transaction scoped. - */ - private PersistenceContext getPersistenceContext(SpiQuery query, SpiTransaction t) { - - // check if there is already a persistence context set which is the case - // when lazy loading or query joins are executed - PersistenceContext ctx = query.getPersistenceContext(); - if (ctx != null) return ctx; - - // determine the scope (from the query and then server) - PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query); - return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext(); - } - - /** - * Will end a locally created transaction. - *

- * It ends the query only transaction. - *

- */ - public void endTransIfRequired() { - if (createdTransaction) { - transaction.endQueryOnly(); - } - } - - /** - * Return true if this is a find by id (rather than List Set or Map). - */ - public boolean isFindById() { - return query.getType() == Type.BEAN; - } - - /** - * Execute the query as findById. - */ - public Object findId() { - return queryEngine.findId(this); - } - - public int findRowCount() { - return queryEngine.findRowCount(this); - } - - public List findIds() { - BeanIdList idList = queryEngine.findIds(this); - return idList.getIdList(); - } - - public void findEach(QueryEachConsumer consumer) { - QueryIterator it = queryEngine.findIterate(this); - try { - while (it.hasNext()) { - consumer.accept(it.next()); - } - } finally { - it.close(); - } - } - - public void findEachWhile(QueryEachWhileConsumer consumer) { - QueryIterator it = queryEngine.findIterate(this); - try { - while (it.hasNext()) { - if (!consumer.accept(it.next())) { - break; - } - } - } finally { - it.close(); - } - } - - public void findVisit(QueryResultVisitor visitor) { - QueryIterator it = queryEngine.findIterate(this); - try { - while (it.hasNext()) { - if (!visitor.accept(it.next())) { - break; - } - } - } finally { - it.close(); - } - } - - public QueryIterator findIterate() { - return queryEngine.findIterate(this); - } - - /** - * Execute the query as findList. - */ - @SuppressWarnings("unchecked") - public List findList() { - return (List) queryEngine.findMany(this); - } - - /** - * Execute the query as findSet. - */ - @SuppressWarnings("unchecked") - public Set findSet() { - return (Set)queryEngine.findMany(this); - } - - /** - * Execute the query as findMap. - */ - public Map findMap() { - String mapKey = query.getMapKey(); - if (mapKey == null) { - BeanProperty idProp = beanDescriptor.getIdProperty(); - if (idProp != null) { - query.setMapKey(idProp.getName()); - } else { - throw new PersistenceException("No mapKey specified for query"); - } - } - return (Map) queryEngine.findMany(this); - } - - public SpiQuery.Type getQueryType() { - return query.getType(); - } - - /** - * Return a bean specific finder if one has been set. - */ - public BeanFinder getBeanFinder() { - return finder; - } - - /** - * Return the find that is to be performed. - */ - public SpiQuery getQuery() { - return query; - } - - /** - * Return the many property that is fetched in the query or null if there is - * not one. - */ - public BeanPropertyAssocMany getManyProperty() { - return beanDescriptor.getManyProperty(query); - } - - /** - * Return a queryPlan for the current query if one exists. Returns null if no - * query plan for this query exists. - */ - public CQueryPlan getQueryPlan() { - return beanDescriptor.getQueryPlan(queryPlanHash); - } - - /** - * Return the queryPlanHash. - *

- * This identifies the query plan for a given bean type. It effectively - * matches a SQL statement with ? bind variables. A query plan can be reused - * with just the bind variables changing. - *

- */ - public HashQueryPlan getQueryPlanHash() { - return queryPlanHash; - } - - /** - * Put the QueryPlan into the cache. - */ - public void putQueryPlan(CQueryPlan queryPlan) { - beanDescriptor.putQueryPlan(queryPlanHash, queryPlan); - } - - public boolean isUseBeanCache() { - return beanDescriptor.calculateUseCache(query.isUseBeanCache()); - } - - /** - * Try to get the query result from the query cache. - */ - public BeanCollection getFromQueryCache() { - - if (!query.isUseQueryCache()) { - return null; - } - - cacheKey = query.queryHash(); - - return beanDescriptor.queryCacheGet(cacheKey); - } - - public void putToQueryCache(BeanCollection queryResult) { - beanDescriptor.queryCachePut(cacheKey, queryResult); - } - - /** - * Set an Query object that owns the PreparedStatement that can be cancelled. - */ - public void setCancelableQuery(CancelableQuery cancelableQuery) { - query.setCancelableQuery(cancelableQuery); - } - - /** - * Log the SQL if the logLevel is appropriate. - */ - public void logSql(String sql) { - transaction.logSql(sql); - } - - /** - * Return true if the request wants to log the secondary queries (test purpose). - */ - public boolean isLogSecondaryQuery() { - return query.isLogSecondaryQuery(); - } - - /** - * Return the batch size for lazy loading on this bean query request. - */ - public int getLazyLoadBatchSize() { - - int batchSize = query.getLazyLoadBatchSize(); - return (batchSize > 0) ? batchSize : ebeanServer.getLazyLoadBatchSize(); - } -} +package com.avaje.ebeaninternal.server.core; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.*; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebean.event.BeanQueryRequest; +import com.avaje.ebeaninternal.api.BeanIdList; +import com.avaje.ebeaninternal.api.HashQuery; +import com.avaje.ebeaninternal.api.HashQueryPlan; +import com.avaje.ebeaninternal.api.LoadContext; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Type; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.DeployParser; +import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap; +import com.avaje.ebeaninternal.server.loadcontext.DLoadContext; +import com.avaje.ebeaninternal.server.query.CQueryPlan; +import com.avaje.ebeaninternal.server.query.CancelableQuery; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; + +/** + * Wraps the objects involved in executing a Query. + */ +public final class OrmQueryRequest extends BeanRequest implements BeanQueryRequest, SpiOrmQueryRequest { + + private final BeanDescriptor beanDescriptor; + + private final OrmQueryEngine queryEngine; + + private final SpiQuery query; + + private final BeanFinder finder; + + private final Boolean readOnly; + + private final RawSql rawSql; + + private LoadContext loadContext; + + private PersistenceContext persistenceContext; + + private HashQuery cacheKey; + + private HashQueryPlan queryPlanHash; + + /** + * Create the InternalQueryRequest. + */ + public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery query, BeanDescriptor desc, SpiTransaction t) { + + super(server, t); + + this.beanDescriptor = desc; + this.rawSql = query.getRawSql(); + this.finder = beanDescriptor.getBeanFinder(); + this.queryEngine = queryEngine; + this.query = query; + this.readOnly = query.isReadOnly(); + } + + + /** + * Return the database platform like clause. + */ + @Override + public String getDBLikeClause() { + return ebeanServer.getDatabasePlatform().getLikeClause(); + } + + public void executeSecondaryQueries() { + loadContext.executeSecondaryQueries(this); + } + + /** + * For use with QueryIterator and secondary queries this returns the minimum + * batch size that should be loaded before executing the secondary queries. + *

+ * If -1 is returned then NO secondary queries are registered and simple + * iteration is fine. + *

+ */ + public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) { + return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch); + } + + /** + * Return the Normal, sharedInstance, ReadOnly state of this query. + */ + public Boolean isReadOnly() { + return readOnly; + } + + /** + * Return the BeanDescriptor for the associated bean. + */ + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + /** + * Return the graph context for this query. + */ + public LoadContext getGraphContext() { + return loadContext; + } + + /** + * Calculate the query plan hash AFTER any potential AutoFetch tuning. + */ + public void calculateQueryPlanHash() { + this.queryPlanHash = query.queryPlanHash(this); + } + + public boolean isRawSql() { + return rawSql != null; + } + + public DeployParser createDeployParser() { + if (rawSql != null) { + return new DeployPropertyParserMap(rawSql.getColumnMapping().getMapping()); + } else { + return beanDescriptor.createDeployPropertyParser(); + } + } + + /** + * Return true if this is a query using generated sql. If false this query + * will use raw sql (Entity bean based on raw sql select). + */ + public boolean isSqlSelect() { + return query.isSqlSelect() && query.getRawSql() == null; + } + + /** + * Return the PersistenceContext used for this request. + */ + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } + + /** + * This will create a local (readOnly) transaction if no current transaction + * exists. + *

+ * A transaction may have been passed in explicitly or currently be active in + * the thread local. If not, then a readOnly transaction is created to execute + * this query. + *

+ */ + @Override + public void initTransIfRequired() { + // first check if the query requires its own transaction + if (transaction == null) { + // maybe a current one + transaction = ebeanServer.getCurrentServerTransaction(); + if (transaction == null) { + // create an implicit transaction to execute this query + transaction = ebeanServer.createQueryTransaction(); + createdTransaction = true; + } + } + // initialise the persistenceContext and loadContext + this.persistenceContext = getPersistenceContext(query, transaction); + this.loadContext = new DLoadContext(this); + this.loadContext.registerSecondaryQueries(query); + } + + /** + * For iterate queries reset the persistenceContext and loadContext. + */ + public void flushPersistenceContextOnIterate() { + persistenceContext = new DefaultPersistenceContext(); + loadContext.resetPersistenceContext(persistenceContext); + } + + /** + * Get the TransactionContext either explicitly set on the query or + * transaction scoped. + */ + private PersistenceContext getPersistenceContext(SpiQuery query, SpiTransaction t) { + + // check if there is already a persistence context set which is the case + // when lazy loading or query joins are executed + PersistenceContext ctx = query.getPersistenceContext(); + if (ctx != null) return ctx; + + // determine the scope (from the query and then server) + PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query); + return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext(); + } + + /** + * Will end a locally created transaction. + *

+ * It ends the query only transaction. + *

+ */ + public void endTransIfRequired() { + if (createdTransaction) { + transaction.endQueryOnly(); + } + } + + /** + * Return true if this is a find by id (rather than List Set or Map). + */ + public boolean isFindById() { + return query.getType() == Type.BEAN; + } + + /** + * Execute the query as findById. + */ + public Object findId() { + return queryEngine.findId(this); + } + + public int findRowCount() { + return queryEngine.findRowCount(this); + } + + public List findIds() { + BeanIdList idList = queryEngine.findIds(this); + return idList.getIdList(); + } + + public void findEach(QueryEachConsumer consumer) { + QueryIterator it = queryEngine.findIterate(this); + try { + while (it.hasNext()) { + consumer.accept(it.next()); + } + } finally { + it.close(); + } + } + + public void findEachWhile(QueryEachWhileConsumer consumer) { + QueryIterator it = queryEngine.findIterate(this); + try { + while (it.hasNext()) { + if (!consumer.accept(it.next())) { + break; + } + } + } finally { + it.close(); + } + } + + public void findVisit(QueryResultVisitor visitor) { + QueryIterator it = queryEngine.findIterate(this); + try { + while (it.hasNext()) { + if (!visitor.accept(it.next())) { + break; + } + } + } finally { + it.close(); + } + } + + public QueryIterator findIterate() { + return queryEngine.findIterate(this); + } + + /** + * Execute the query as findList. + */ + @SuppressWarnings("unchecked") + public List findList() { + return (List) queryEngine.findMany(this); + } + + /** + * Execute the query as findSet. + */ + @SuppressWarnings("unchecked") + public Set findSet() { + return (Set)queryEngine.findMany(this); + } + + /** + * Execute the query as findMap. + */ + public Map findMap() { + String mapKey = query.getMapKey(); + if (mapKey == null) { + BeanProperty idProp = beanDescriptor.getIdProperty(); + if (idProp != null) { + query.setMapKey(idProp.getName()); + } else { + throw new PersistenceException("No mapKey specified for query"); + } + } + return (Map) queryEngine.findMany(this); + } + + public SpiQuery.Type getQueryType() { + return query.getType(); + } + + /** + * Return a bean specific finder if one has been set. + */ + public BeanFinder getBeanFinder() { + return finder; + } + + /** + * Return the find that is to be performed. + */ + public SpiQuery getQuery() { + return query; + } + + /** + * Return the many property that is fetched in the query or null if there is + * not one. + */ + public BeanPropertyAssocMany getManyProperty() { + return beanDescriptor.getManyProperty(query); + } + + /** + * Return a queryPlan for the current query if one exists. Returns null if no + * query plan for this query exists. + */ + public CQueryPlan getQueryPlan() { + return beanDescriptor.getQueryPlan(queryPlanHash); + } + + /** + * Return the queryPlanHash. + *

+ * This identifies the query plan for a given bean type. It effectively + * matches a SQL statement with ? bind variables. A query plan can be reused + * with just the bind variables changing. + *

+ */ + public HashQueryPlan getQueryPlanHash() { + return queryPlanHash; + } + + /** + * Put the QueryPlan into the cache. + */ + public void putQueryPlan(CQueryPlan queryPlan) { + beanDescriptor.putQueryPlan(queryPlanHash, queryPlan); + } + + public boolean isUseBeanCache() { + return beanDescriptor.calculateUseCache(query.isUseBeanCache()); + } + + /** + * Try to get the query result from the query cache. + */ + public BeanCollection getFromQueryCache() { + + if (!query.isUseQueryCache()) { + return null; + } + + cacheKey = query.queryHash(); + + return beanDescriptor.queryCacheGet(cacheKey); + } + + public void putToQueryCache(BeanCollection queryResult) { + beanDescriptor.queryCachePut(cacheKey, queryResult); + } + + /** + * Set an Query object that owns the PreparedStatement that can be cancelled. + */ + public void setCancelableQuery(CancelableQuery cancelableQuery) { + query.setCancelableQuery(cancelableQuery); + } + + /** + * Log the SQL if the logLevel is appropriate. + */ + public void logSql(String sql) { + transaction.logSql(sql); + } + + /** + * Return true if the request wants to log the secondary queries (test purpose). + */ + public boolean isLogSecondaryQuery() { + return query.isLogSecondaryQuery(); + } + + /** + * Return the batch size for lazy loading on this bean query request. + */ + public int getLazyLoadBatchSize() { + + int batchSize = query.getLazyLoadBatchSize(); + return (batchSize > 0) ? batchSize : ebeanServer.getLazyLoadBatchSize(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java index e6c0bfad6..1eebc690c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java @@ -1,108 +1,108 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.persist.BatchControl; -import com.avaje.ebeaninternal.server.persist.BatchPostExecute; -import com.avaje.ebeaninternal.server.persist.PersistExecute; - -/** - * Wraps all the objects used to persist a bean. - */ -public abstract class PersistRequest extends BeanRequest implements BatchPostExecute { - - public enum Type { - DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL - } - - protected boolean persistCascade; - - /** - * One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL. - */ - protected Type type; - - protected final PersistExecute persistExecute; - - /** - * Used by CallableSqlRequest and UpdateSqlRequest. - */ - public PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) { - super(server, t); - this.persistExecute = persistExecute; - } - - /** - * Execute a the request or queue/batch it for later execution. - */ - public abstract int executeOrQueue(); - - /** - * Execute the request right now. - */ - public abstract int executeNow(); - - public PstmtBatch getPstmtBatch() { - return ebeanServer.getPstmtBatch(); - } - - public boolean isLogSql() { - return transaction.isLogSql(); - } - - public boolean isLogSummary() { - return transaction.isLogSummary(); - } - - - /** - * Return true if this persist request should use JDBC batch. - */ - public boolean isBatchThisRequest() { - return transaction.isBatchThisRequest(type); - } - - /** - * Execute the statement. - */ - public int executeStatement() { - - boolean batch = isBatchThisRequest(); - - int rows; - BatchControl control = transaction.getBatchControl(); - if (control != null) { - rows = control.executeStatementOrBatch(this, batch); - - } else if (batch) { - // need to create the BatchControl - control = persistExecute.createBatchControl(transaction); - rows = control.executeStatementOrBatch(this, true); - } else { - rows = executeNow(); - } - - return rows; - } - - public void initTransIfRequired() { - createImplicitTransIfRequired(false); - persistCascade = transaction.isPersistCascade(); - } - - /** - * Return the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL - * or CALLABLESQL. - */ - public Type getType() { - return type; - } - - /** - * Return true if save and delete should cascade. - */ - public boolean isPersistCascade() { - return persistCascade; - } - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.persist.BatchControl; +import com.avaje.ebeaninternal.server.persist.BatchPostExecute; +import com.avaje.ebeaninternal.server.persist.PersistExecute; + +/** + * Wraps all the objects used to persist a bean. + */ +public abstract class PersistRequest extends BeanRequest implements BatchPostExecute { + + public enum Type { + DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL + } + + protected boolean persistCascade; + + /** + * One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL. + */ + protected Type type; + + protected final PersistExecute persistExecute; + + /** + * Used by CallableSqlRequest and UpdateSqlRequest. + */ + public PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) { + super(server, t); + this.persistExecute = persistExecute; + } + + /** + * Execute a the request or queue/batch it for later execution. + */ + public abstract int executeOrQueue(); + + /** + * Execute the request right now. + */ + public abstract int executeNow(); + + public PstmtBatch getPstmtBatch() { + return ebeanServer.getPstmtBatch(); + } + + public boolean isLogSql() { + return transaction.isLogSql(); + } + + public boolean isLogSummary() { + return transaction.isLogSummary(); + } + + + /** + * Return true if this persist request should use JDBC batch. + */ + public boolean isBatchThisRequest() { + return transaction.isBatchThisRequest(type); + } + + /** + * Execute the statement. + */ + public int executeStatement() { + + boolean batch = isBatchThisRequest(); + + int rows; + BatchControl control = transaction.getBatchControl(); + if (control != null) { + rows = control.executeStatementOrBatch(this, batch); + + } else if (batch) { + // need to create the BatchControl + control = persistExecute.createBatchControl(transaction); + rows = control.executeStatementOrBatch(this, true); + } else { + rows = executeNow(); + } + + return rows; + } + + public void initTransIfRequired() { + createImplicitTransIfRequired(false); + persistCascade = transaction.isPersistCascade(); + } + + /** + * Return the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL + * or CALLABLESQL. + */ + public Type getType() { + return type; + } + + /** + * Return true if save and delete should cascade. + */ + public boolean isPersistCascade() { + return persistCascade; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index d831623c0..7fc55361e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -1,709 +1,709 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.OptimisticLockException; - -import com.avaje.ebean.ValuePair; -import com.avaje.ebean.annotation.ConcurrencyMode; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanPersistRequest; -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEvent; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.persist.BatchControl; -import com.avaje.ebeaninternal.server.persist.PersistExecute; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; -import com.avaje.ebeaninternal.server.transaction.BeanDelta; -import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap; - -/** - * PersistRequest for insert update or delete of a bean. - */ -public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest { - - private final BeanManager beanManager; - - private final BeanDescriptor beanDescriptor; - - private final BeanPersistListener beanPersistListener; - - /** - * For per post insert update delete control. - */ - private final BeanPersistController controller; - - /** - * The bean being persisted. - */ - private final T bean; - - private final EntityBean entityBean; - - /** - * The associated intercept. - */ - private final EntityBeanIntercept intercept; - - /** - * The parent bean for unidirectional save. - */ - private final Object parentBean; - - private final boolean dirty; - - private ConcurrencyMode concurrencyMode; - - /** - * The unique id used for logging summary. - */ - private Object idValue; - - /** - * Hash value used to handle cascade delete both ways in a relationship. - */ - private Integer beanHash; - - /** - * Flag set if this is a stateless update. - */ - private boolean statelessUpdate; - - private boolean notifyCache; - - private boolean deleteMissingChildren; - - /** - * Flag used to detect when only many properties where updated via a cascade. Used to ensure - * appropriate caches are updated in that case. - */ - private boolean updatedManysOnly; - - /** - * Many properties that were cascade saved (and hence might need caches updated later). - */ - private List> updatedManys; - - /** - * Need to get and store the updated properties because the persist listener is notified - * later on a different thread and the bean has been reset at that point. - */ - private Set updatedProperties; - - /** - * Flag set when request is added to JDBC batch. - */ - private boolean batched; - - /** - * Flag set when batchOnCascade to avoid using batch on the top bean. - */ - private boolean skipBatchForTopLevel; - - /** - * Flag set when batch mode is turned on for a persist cascade. - */ - private boolean batchOnCascadeSet; - - public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, - PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) { - - super(server, t, persistExecute); - this.entityBean = (EntityBean) bean; - this.intercept = entityBean._ebean_getIntercept(); - this.beanManager = mgr; - this.beanDescriptor = mgr.getBeanDescriptor(); - this.beanPersistListener = beanDescriptor.getPersistListener(); - this.bean = bean; - this.parentBean = parentBean; - this.controller = beanDescriptor.getPersistController(); - this.type = type; - - if (saveRecurse) { - this.persistCascade = t.isPersistCascade(); - } - - if (this.type == Type.UPDATE) { - if (intercept.isNew()) { - // 'stateless update' - set loaded properties as dirty - intercept.setNewBeanForUpdate(); - statelessUpdate = true; - } - // Mark Mutable scalar properties (like Hstore) as dirty where necessary - beanDescriptor.checkMutableProperties(intercept); - } - this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept); - this.dirty = intercept.isDirty(); - } - - /** - * Init the transaction and also check for batch on cascade escalation. - */ - public void initTransIfRequiredWithBatchCascade() { - createImplicitTransIfRequired(false); - if (transaction.checkBatchEscalationOnCascade(this)) { - // we escalated to use batch mode so flush when done - // but if createdTransaction then commit will flush it - batchOnCascadeSet = !createdTransaction; - } - persistCascade = transaction.isPersistCascade(); - } - - /** - * If using batch on cascade flush if required. - */ - public void flushBatchOnCascade() { - if (batchOnCascadeSet) { - // we escalated to batch mode for request so flush - transaction.flushBatchOnCascade(); - batchOnCascadeSet = false; - } - } - - /** - * Return true is this request was added to the JDBC batch. - */ - public boolean isBatched() { - return batched; - } - - /** - * Set when request is added to the JDBC batch. - */ - public void setBatched() { - batched = true; - } - - - public void setSkipBatchForTopLevel() { - skipBatchForTopLevel = true; - } - - @Override - public boolean isBatchThisRequest() { - return !skipBatchForTopLevel && super.isBatchThisRequest(); - } - - /** - * Return true if this is an insert request. - */ - public boolean isInsert() { - return Type.INSERT == type; - } - - @Override - public Set getLoadedProperties() { - return intercept.getLoadedPropertyNames(); - } - - @Override - public Set getUpdatedProperties() { - return intercept.getDirtyPropertyNames(); - } - - @Override - public Map getUpdatedValues() { - return intercept.getDirtyValues(); - } - - public boolean isNotify() { - this.notifyCache = beanDescriptor.isCacheNotify(); - return notifyCache || isNotifyPersistListener(); - } - - public boolean isNotifyPersistListener() { - return beanPersistListener != null; - } - - /** - * Notify/Update the local L2 cache after the transaction has successfully committed. - */ - public void notifyCache() { - if (notifyCache) { - switch (type) { - case INSERT: - beanDescriptor.cacheHandleInsert(idValue, this); - break; - case UPDATE: - beanDescriptor.cacheHandleUpdate(idValue, this); - break; - case DELETE: - // Bean deleted from cache early via postDelete() - break; - default: - throw new IllegalStateException("Invalid type " + type); - } - } - } - - public void addToPersistMap(BeanPersistIdMap beanPersistMap) { - - beanPersistMap.add(beanDescriptor, type, idValue); - } - - public boolean notifyLocalPersistListener() { - if (beanPersistListener == null) { - return false; - - } else { - switch (type) { - case INSERT: - return beanPersistListener.inserted(bean); - - case UPDATE: - return beanPersistListener.updated(bean, updatedProperties); - - case DELETE: - return beanPersistListener.deleted(bean); - - default: - return false; - } - } - } - - public boolean isParent(Object o) { - return o == parentBean; - } - - /** - * Return true if this bean has been already been persisted (inserted or updated) in this - * transaction. - */ - public boolean isRegisteredBean() { - return transaction.isRegisteredBean(bean); - } - - public void unRegisterBean() { - transaction.unregisterBean(bean); - } - - /** - * The hash used to register the bean with the transaction. - *

- * Takes into account the class type and id value. - *

- */ - private Integer getBeanHash() { - if (beanHash == null) { - Object id = beanDescriptor.getId(entityBean); - int hc = 31 * bean.getClass().getName().hashCode(); - if (id != null) { - hc += id.hashCode(); - } - beanHash = new Integer(hc); - } - return beanHash; - } - - public void registerDeleteBean() { - Integer hash = getBeanHash(); - transaction.registerDeleteBean(hash); - } - - public void unregisterDeleteBean() { - Integer hash = getBeanHash(); - transaction.unregisterDeleteBean(hash); - } - - public boolean isRegisteredForDeleteBean() { - if (transaction == null) { - return false; - } else { - Integer hash = getBeanHash(); - return transaction.isRegisteredDeleteBean(hash); - } - } - - public BeanManager getBeanManager() { - return beanManager; - } - - /** - * Return the BeanDescriptor for the associated bean. - */ - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - /** - * Return true if a stateless update should also delete any missing details beans. - */ - public boolean isDeleteMissingChildren() { - return deleteMissingChildren; - } - - /** - * Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany. - */ - public void setDeleteMissingChildren(boolean deleteMissingChildren) { - this.deleteMissingChildren = deleteMissingChildren; - } - - /** - * Prepare the update after potential modifications in a BeanPersistController. - */ - public void postControllerPrepareUpdate() { - if (intercept.isNew() && controller != null) { - // 'stateless update' - set dirty properties modified in controller preUpdate - intercept.setNewBeanForUpdate(); - } - } - - /** - * Used to skip updates if we know the bean is not dirty. This is the case for EntityBeans that - * have not been modified. - */ - public boolean isDirty() { - return dirty; - } - - /** - * Return the concurrency mode used for this persist. - */ - public ConcurrencyMode getConcurrencyMode() { - return concurrencyMode; - } - - /** - * Returns a description of the request. This is typically the bean class name or the base table - * for MapBeans. - *

- * Used to determine common persist requests for queueing and statement batching. - *

- */ - public String getFullName() { - return beanDescriptor.getFullName(); - } - - /** - * Return the bean associated with this request. - */ - public T getBean() { - return bean; - } - - public EntityBean getEntityBean() { - return entityBean; - } - - /** - * Return the Id value for the bean. - */ - public Object getBeanId() { - return beanDescriptor.getId(entityBean); - } - - public BeanDelta createDeltaBean() { - return new BeanDelta(beanDescriptor, getBeanId()); - } - - /** - * Return the parent bean for cascading save with unidirectional relationship. - */ - public Object getParentBean() { - return parentBean; - } - - /** - * Return the controller if there is one associated with this type of bean. This returns null if - * there is no controller associated. - */ - public BeanPersistController getBeanController() { - return controller; - } - - /** - * Return the intercept if there is one. - */ - public EntityBeanIntercept getEntityBeanIntercept() { - return intercept; - } - - /** - * Return true if this property is loaded (full bean or included in partial bean). - */ - public boolean isLoadedProperty(BeanProperty prop) { - return intercept.isLoadedProperty(prop.getPropertyIndex()); - } - - @Override - public int executeNow() { - switch (type) { - case INSERT: - persistExecute.executeInsertBean(this); - return -1; - - case UPDATE: - if (beanPersistListener != null) { - // store the updated properties for sending later - updatedProperties = getUpdatedProperties(); - } - persistExecute.executeUpdateBean(this); - return -1; - - case DELETE: - persistExecute.executeDeleteBean(this); - return -1; - - default: - throw new RuntimeException("Invalid type " + type); - } - } - - @Override - public int executeOrQueue() { - - boolean batch = isBatchThisRequest(); - - BatchControl control = transaction.getBatchControl(); - if (control != null) { - return control.executeOrQueue(this, batch); - } - if (batch) { - control = persistExecute.createBatchControl(transaction); - return control.executeOrQueue(this, true); - - } else { - return executeNow(); - } - } - - /** - * Set the generated key back to the bean. Only used for inserts with getGeneratedKeys. - */ - public void setGeneratedKey(Object idValue) { - if (idValue != null) { - // remember it for logging summary - this.idValue = beanDescriptor.convertSetId(idValue, entityBean); - } - } - - /** - * Set the Id value that was bound. Used for the purposes of logging summary information on this - * request. - */ - public void setBoundId(Object idValue) { - this.idValue = idValue; - } - - /** - * Check for optimistic concurrency exception. - */ - public final void checkRowCount(int rowCount) throws SQLException { - if (rowCount != 1) { - String m = Message.msg("persist.conc2", "" + rowCount); - throw new OptimisticLockException(m, null, bean); - } - switch (type) { - case DELETE: postDelete(); break; - case UPDATE: postUpdate(); break; - default: // do nothing - } - } - - /** - * Clear the bean from the PersistenceContext (L1 cache) for stateless updates. - */ - private void postUpdate() { - if (statelessUpdate) { - transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue); - } - } - - /** - * Aggressive L1 and L2 cache cleanup for deletes. - */ - private void postDelete() { - // Delete the bean from the PersistenceContent - transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue); - // Delete from cache early even if transaction fails - beanDescriptor.cacheHandleDelete(idValue, this); - } - - /** - * Post processing. - */ - public void postExecute() throws SQLException { - - if (controller != null) { - controllerPost(); - } - - // if bean persisted again then should result in an update - intercept.setLoaded(); - - addEvent(); - - if (isLogSummary()) { - logSummary(); - } - } - - private void controllerPost() { - switch (type) { - case INSERT: - controller.postInsert(this); - break; - case UPDATE: - controller.postUpdate(this); - break; - case DELETE: - controller.postDelete(this); - break; - default: - break; - } - } - - private void logSummary() { - - String name = beanDescriptor.getName(); - switch (type) { - case INSERT: - transaction.logSummary("Inserted [" + name + "] [" + idValue + "]"); - break; - case UPDATE: - transaction.logSummary("Updated [" + name + "] [" + idValue + "]"); - break; - case DELETE: - transaction.logSummary("Deleted [" + name + "] [" + idValue + "]"); - break; - default: - break; - } - } - - /** - * Add the bean to the TransactionEvent. This will be used by TransactionManager to synch Cache, - * Cluster and text indexes. - */ - private void addEvent() { - - TransactionEvent event = transaction.getEvent(); - if (event != null) { - event.add(this); - } - } - - /** - * Determine the concurrency mode depending on fully/partially populated bean. - *

- * Specifically with version concurrency we want to check that the version property was one of the - * loaded properties. - *

- */ - public ConcurrencyMode determineConcurrencyMode() { - - // 'partial bean' update/delete... - if (concurrencyMode.equals(ConcurrencyMode.VERSION)) { - // check the version property was loaded - BeanProperty prop = beanDescriptor.getVersionProperty(); - if (prop == null || !intercept.isLoadedProperty(prop.getPropertyIndex())) { - concurrencyMode = ConcurrencyMode.NONE; - } - } - - return concurrencyMode; - } - - /** - * Return true if the update DML/SQL must be dynamically generated. - *

- * This is the case for updates/deletes of partially populated beans. - *

- */ - public boolean isDynamicUpdateSql() { - return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean(); - } - - /** - * Create a GenerateDmlRequest used to generate the DML. - *

- * Will used changed properties or loaded properties depending on the - * BeanDescriptor.isUpdateChangesOnly() value. - *

- */ - public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) { - return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly()); - } - - /** - * Test if the property value has changed and if so include it in the update. - */ - public boolean isAddToUpdate(BeanProperty prop) { - return intercept.isDirtyProperty(prop.getPropertyIndex()); - } - - public List getDerivedRelationships() { - return transaction.getDerivedRelationship(bean); - } - - public void postInsert() { - // mark all properties as loaded after an insert to support immediate update - int len = intercept.getPropertyLength(); - for (int i = 0; i < len; i++) { - intercept.setLoadedProperty(i); - } - } - - public boolean isReference() { - return beanDescriptor.isReference(intercept); - } - - /** - * This many property has been cascade saved. Keep note of this and update the 'many property' - * cache on post commit. - */ - public void addUpdatedManyProperty(BeanPropertyAssocMany updatedAssocMany) { - // if (notifyCache) { - if (updatedManys == null) { - updatedManys = new ArrayList>(5); - } - updatedManys.add(updatedAssocMany); - // } - } - - /** - * Return the list of cascade updated many properties (can be null). - */ - public List> getUpdatedManyCollections() { - return updatedManys; - } - - /** - * Check if any of its many properties where cascade saved and hence we need to update related - * many property caches. - */ - public void checkUpdatedManysOnly() { - if (!dirty && updatedManys != null) { - // set the flag and register for post commit processing if there - // is caching or registered listeners - if (idValue == null) { - this.idValue = beanDescriptor.getId(entityBean); - } - updatedManysOnly = true; - addEvent(); - } - } - - /** - * Return true if only many properties where updated. - */ - public boolean isUpdatedManysOnly() { - return updatedManysOnly; - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.OptimisticLockException; + +import com.avaje.ebean.ValuePair; +import com.avaje.ebean.annotation.ConcurrencyMode; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanPersistRequest; +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEvent; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.persist.BatchControl; +import com.avaje.ebeaninternal.server.persist.PersistExecute; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; +import com.avaje.ebeaninternal.server.transaction.BeanDelta; +import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap; + +/** + * PersistRequest for insert update or delete of a bean. + */ +public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest { + + private final BeanManager beanManager; + + private final BeanDescriptor beanDescriptor; + + private final BeanPersistListener beanPersistListener; + + /** + * For per post insert update delete control. + */ + private final BeanPersistController controller; + + /** + * The bean being persisted. + */ + private final T bean; + + private final EntityBean entityBean; + + /** + * The associated intercept. + */ + private final EntityBeanIntercept intercept; + + /** + * The parent bean for unidirectional save. + */ + private final Object parentBean; + + private final boolean dirty; + + private ConcurrencyMode concurrencyMode; + + /** + * The unique id used for logging summary. + */ + private Object idValue; + + /** + * Hash value used to handle cascade delete both ways in a relationship. + */ + private Integer beanHash; + + /** + * Flag set if this is a stateless update. + */ + private boolean statelessUpdate; + + private boolean notifyCache; + + private boolean deleteMissingChildren; + + /** + * Flag used to detect when only many properties where updated via a cascade. Used to ensure + * appropriate caches are updated in that case. + */ + private boolean updatedManysOnly; + + /** + * Many properties that were cascade saved (and hence might need caches updated later). + */ + private List> updatedManys; + + /** + * Need to get and store the updated properties because the persist listener is notified + * later on a different thread and the bean has been reset at that point. + */ + private Set updatedProperties; + + /** + * Flag set when request is added to JDBC batch. + */ + private boolean batched; + + /** + * Flag set when batchOnCascade to avoid using batch on the top bean. + */ + private boolean skipBatchForTopLevel; + + /** + * Flag set when batch mode is turned on for a persist cascade. + */ + private boolean batchOnCascadeSet; + + public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, + PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) { + + super(server, t, persistExecute); + this.entityBean = (EntityBean) bean; + this.intercept = entityBean._ebean_getIntercept(); + this.beanManager = mgr; + this.beanDescriptor = mgr.getBeanDescriptor(); + this.beanPersistListener = beanDescriptor.getPersistListener(); + this.bean = bean; + this.parentBean = parentBean; + this.controller = beanDescriptor.getPersistController(); + this.type = type; + + if (saveRecurse) { + this.persistCascade = t.isPersistCascade(); + } + + if (this.type == Type.UPDATE) { + if (intercept.isNew()) { + // 'stateless update' - set loaded properties as dirty + intercept.setNewBeanForUpdate(); + statelessUpdate = true; + } + // Mark Mutable scalar properties (like Hstore) as dirty where necessary + beanDescriptor.checkMutableProperties(intercept); + } + this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept); + this.dirty = intercept.isDirty(); + } + + /** + * Init the transaction and also check for batch on cascade escalation. + */ + public void initTransIfRequiredWithBatchCascade() { + createImplicitTransIfRequired(false); + if (transaction.checkBatchEscalationOnCascade(this)) { + // we escalated to use batch mode so flush when done + // but if createdTransaction then commit will flush it + batchOnCascadeSet = !createdTransaction; + } + persistCascade = transaction.isPersistCascade(); + } + + /** + * If using batch on cascade flush if required. + */ + public void flushBatchOnCascade() { + if (batchOnCascadeSet) { + // we escalated to batch mode for request so flush + transaction.flushBatchOnCascade(); + batchOnCascadeSet = false; + } + } + + /** + * Return true is this request was added to the JDBC batch. + */ + public boolean isBatched() { + return batched; + } + + /** + * Set when request is added to the JDBC batch. + */ + public void setBatched() { + batched = true; + } + + + public void setSkipBatchForTopLevel() { + skipBatchForTopLevel = true; + } + + @Override + public boolean isBatchThisRequest() { + return !skipBatchForTopLevel && super.isBatchThisRequest(); + } + + /** + * Return true if this is an insert request. + */ + public boolean isInsert() { + return Type.INSERT == type; + } + + @Override + public Set getLoadedProperties() { + return intercept.getLoadedPropertyNames(); + } + + @Override + public Set getUpdatedProperties() { + return intercept.getDirtyPropertyNames(); + } + + @Override + public Map getUpdatedValues() { + return intercept.getDirtyValues(); + } + + public boolean isNotify() { + this.notifyCache = beanDescriptor.isCacheNotify(); + return notifyCache || isNotifyPersistListener(); + } + + public boolean isNotifyPersistListener() { + return beanPersistListener != null; + } + + /** + * Notify/Update the local L2 cache after the transaction has successfully committed. + */ + public void notifyCache() { + if (notifyCache) { + switch (type) { + case INSERT: + beanDescriptor.cacheHandleInsert(idValue, this); + break; + case UPDATE: + beanDescriptor.cacheHandleUpdate(idValue, this); + break; + case DELETE: + // Bean deleted from cache early via postDelete() + break; + default: + throw new IllegalStateException("Invalid type " + type); + } + } + } + + public void addToPersistMap(BeanPersistIdMap beanPersistMap) { + + beanPersistMap.add(beanDescriptor, type, idValue); + } + + public boolean notifyLocalPersistListener() { + if (beanPersistListener == null) { + return false; + + } else { + switch (type) { + case INSERT: + return beanPersistListener.inserted(bean); + + case UPDATE: + return beanPersistListener.updated(bean, updatedProperties); + + case DELETE: + return beanPersistListener.deleted(bean); + + default: + return false; + } + } + } + + public boolean isParent(Object o) { + return o == parentBean; + } + + /** + * Return true if this bean has been already been persisted (inserted or updated) in this + * transaction. + */ + public boolean isRegisteredBean() { + return transaction.isRegisteredBean(bean); + } + + public void unRegisterBean() { + transaction.unregisterBean(bean); + } + + /** + * The hash used to register the bean with the transaction. + *

+ * Takes into account the class type and id value. + *

+ */ + private Integer getBeanHash() { + if (beanHash == null) { + Object id = beanDescriptor.getId(entityBean); + int hc = 31 * bean.getClass().getName().hashCode(); + if (id != null) { + hc += id.hashCode(); + } + beanHash = new Integer(hc); + } + return beanHash; + } + + public void registerDeleteBean() { + Integer hash = getBeanHash(); + transaction.registerDeleteBean(hash); + } + + public void unregisterDeleteBean() { + Integer hash = getBeanHash(); + transaction.unregisterDeleteBean(hash); + } + + public boolean isRegisteredForDeleteBean() { + if (transaction == null) { + return false; + } else { + Integer hash = getBeanHash(); + return transaction.isRegisteredDeleteBean(hash); + } + } + + public BeanManager getBeanManager() { + return beanManager; + } + + /** + * Return the BeanDescriptor for the associated bean. + */ + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + /** + * Return true if a stateless update should also delete any missing details beans. + */ + public boolean isDeleteMissingChildren() { + return deleteMissingChildren; + } + + /** + * Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany. + */ + public void setDeleteMissingChildren(boolean deleteMissingChildren) { + this.deleteMissingChildren = deleteMissingChildren; + } + + /** + * Prepare the update after potential modifications in a BeanPersistController. + */ + public void postControllerPrepareUpdate() { + if (intercept.isNew() && controller != null) { + // 'stateless update' - set dirty properties modified in controller preUpdate + intercept.setNewBeanForUpdate(); + } + } + + /** + * Used to skip updates if we know the bean is not dirty. This is the case for EntityBeans that + * have not been modified. + */ + public boolean isDirty() { + return dirty; + } + + /** + * Return the concurrency mode used for this persist. + */ + public ConcurrencyMode getConcurrencyMode() { + return concurrencyMode; + } + + /** + * Returns a description of the request. This is typically the bean class name or the base table + * for MapBeans. + *

+ * Used to determine common persist requests for queueing and statement batching. + *

+ */ + public String getFullName() { + return beanDescriptor.getFullName(); + } + + /** + * Return the bean associated with this request. + */ + public T getBean() { + return bean; + } + + public EntityBean getEntityBean() { + return entityBean; + } + + /** + * Return the Id value for the bean. + */ + public Object getBeanId() { + return beanDescriptor.getId(entityBean); + } + + public BeanDelta createDeltaBean() { + return new BeanDelta(beanDescriptor, getBeanId()); + } + + /** + * Return the parent bean for cascading save with unidirectional relationship. + */ + public Object getParentBean() { + return parentBean; + } + + /** + * Return the controller if there is one associated with this type of bean. This returns null if + * there is no controller associated. + */ + public BeanPersistController getBeanController() { + return controller; + } + + /** + * Return the intercept if there is one. + */ + public EntityBeanIntercept getEntityBeanIntercept() { + return intercept; + } + + /** + * Return true if this property is loaded (full bean or included in partial bean). + */ + public boolean isLoadedProperty(BeanProperty prop) { + return intercept.isLoadedProperty(prop.getPropertyIndex()); + } + + @Override + public int executeNow() { + switch (type) { + case INSERT: + persistExecute.executeInsertBean(this); + return -1; + + case UPDATE: + if (beanPersistListener != null) { + // store the updated properties for sending later + updatedProperties = getUpdatedProperties(); + } + persistExecute.executeUpdateBean(this); + return -1; + + case DELETE: + persistExecute.executeDeleteBean(this); + return -1; + + default: + throw new RuntimeException("Invalid type " + type); + } + } + + @Override + public int executeOrQueue() { + + boolean batch = isBatchThisRequest(); + + BatchControl control = transaction.getBatchControl(); + if (control != null) { + return control.executeOrQueue(this, batch); + } + if (batch) { + control = persistExecute.createBatchControl(transaction); + return control.executeOrQueue(this, true); + + } else { + return executeNow(); + } + } + + /** + * Set the generated key back to the bean. Only used for inserts with getGeneratedKeys. + */ + public void setGeneratedKey(Object idValue) { + if (idValue != null) { + // remember it for logging summary + this.idValue = beanDescriptor.convertSetId(idValue, entityBean); + } + } + + /** + * Set the Id value that was bound. Used for the purposes of logging summary information on this + * request. + */ + public void setBoundId(Object idValue) { + this.idValue = idValue; + } + + /** + * Check for optimistic concurrency exception. + */ + public final void checkRowCount(int rowCount) throws SQLException { + if (rowCount != 1) { + String m = Message.msg("persist.conc2", "" + rowCount); + throw new OptimisticLockException(m, null, bean); + } + switch (type) { + case DELETE: postDelete(); break; + case UPDATE: postUpdate(); break; + default: // do nothing + } + } + + /** + * Clear the bean from the PersistenceContext (L1 cache) for stateless updates. + */ + private void postUpdate() { + if (statelessUpdate) { + transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue); + } + } + + /** + * Aggressive L1 and L2 cache cleanup for deletes. + */ + private void postDelete() { + // Delete the bean from the PersistenceContent + transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue); + // Delete from cache early even if transaction fails + beanDescriptor.cacheHandleDelete(idValue, this); + } + + /** + * Post processing. + */ + public void postExecute() throws SQLException { + + if (controller != null) { + controllerPost(); + } + + // if bean persisted again then should result in an update + intercept.setLoaded(); + + addEvent(); + + if (isLogSummary()) { + logSummary(); + } + } + + private void controllerPost() { + switch (type) { + case INSERT: + controller.postInsert(this); + break; + case UPDATE: + controller.postUpdate(this); + break; + case DELETE: + controller.postDelete(this); + break; + default: + break; + } + } + + private void logSummary() { + + String name = beanDescriptor.getName(); + switch (type) { + case INSERT: + transaction.logSummary("Inserted [" + name + "] [" + idValue + "]"); + break; + case UPDATE: + transaction.logSummary("Updated [" + name + "] [" + idValue + "]"); + break; + case DELETE: + transaction.logSummary("Deleted [" + name + "] [" + idValue + "]"); + break; + default: + break; + } + } + + /** + * Add the bean to the TransactionEvent. This will be used by TransactionManager to synch Cache, + * Cluster and text indexes. + */ + private void addEvent() { + + TransactionEvent event = transaction.getEvent(); + if (event != null) { + event.add(this); + } + } + + /** + * Determine the concurrency mode depending on fully/partially populated bean. + *

+ * Specifically with version concurrency we want to check that the version property was one of the + * loaded properties. + *

+ */ + public ConcurrencyMode determineConcurrencyMode() { + + // 'partial bean' update/delete... + if (concurrencyMode.equals(ConcurrencyMode.VERSION)) { + // check the version property was loaded + BeanProperty prop = beanDescriptor.getVersionProperty(); + if (prop == null || !intercept.isLoadedProperty(prop.getPropertyIndex())) { + concurrencyMode = ConcurrencyMode.NONE; + } + } + + return concurrencyMode; + } + + /** + * Return true if the update DML/SQL must be dynamically generated. + *

+ * This is the case for updates/deletes of partially populated beans. + *

+ */ + public boolean isDynamicUpdateSql() { + return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean(); + } + + /** + * Create a GenerateDmlRequest used to generate the DML. + *

+ * Will used changed properties or loaded properties depending on the + * BeanDescriptor.isUpdateChangesOnly() value. + *

+ */ + public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) { + return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly()); + } + + /** + * Test if the property value has changed and if so include it in the update. + */ + public boolean isAddToUpdate(BeanProperty prop) { + return intercept.isDirtyProperty(prop.getPropertyIndex()); + } + + public List getDerivedRelationships() { + return transaction.getDerivedRelationship(bean); + } + + public void postInsert() { + // mark all properties as loaded after an insert to support immediate update + int len = intercept.getPropertyLength(); + for (int i = 0; i < len; i++) { + intercept.setLoadedProperty(i); + } + } + + public boolean isReference() { + return beanDescriptor.isReference(intercept); + } + + /** + * This many property has been cascade saved. Keep note of this and update the 'many property' + * cache on post commit. + */ + public void addUpdatedManyProperty(BeanPropertyAssocMany updatedAssocMany) { + // if (notifyCache) { + if (updatedManys == null) { + updatedManys = new ArrayList>(5); + } + updatedManys.add(updatedAssocMany); + // } + } + + /** + * Return the list of cascade updated many properties (can be null). + */ + public List> getUpdatedManyCollections() { + return updatedManys; + } + + /** + * Check if any of its many properties where cascade saved and hence we need to update related + * many property caches. + */ + public void checkUpdatedManysOnly() { + if (!dirty && updatedManys != null) { + // set the flag and register for post commit processing if there + // is caching or registered listeners + if (idValue == null) { + this.idValue = beanDescriptor.getId(entityBean); + } + updatedManysOnly = true; + addEvent(); + } + } + + /** + * Return true if only many properties where updated. + */ + public boolean isUpdatedManysOnly() { + return updatedManysOnly; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java index 4f8b0b619..3acc84255 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java @@ -1,147 +1,147 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.CallableStatement; -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebean.CallableSql; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.BindParams.Param; -import com.avaje.ebeaninternal.api.SpiCallableSql; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.server.persist.PersistExecute; - -/** - * Persist request specifically for CallableSql. - */ -public final class PersistRequestCallableSql extends PersistRequest { - - private final SpiCallableSql callableSql; - - private int rowCount; - - private String bindLog; - - private CallableStatement cstmt; - - private BindParams bindParam; - - /** - * Create. - */ - public PersistRequestCallableSql(SpiEbeanServer server, - CallableSql cs, SpiTransaction t, PersistExecute persistExecute) { - - super(server, t, persistExecute); - this.type = PersistRequest.Type.CALLABLESQL; - this.callableSql = (SpiCallableSql)cs; - } - - @Override - public int executeOrQueue() { - return executeStatement(); - } - - @Override - public int executeNow() { - return persistExecute.executeSqlCallable(this); - } - - /** - * Return the CallableSql. - */ - public SpiCallableSql getCallableSql() { - return callableSql; - } - - /** - * The the log of bind values. - */ - public void setBindLog(String bindLog) { - this.bindLog = bindLog; - } - - /** - * Note the rowCount of the execution. - */ - public void checkRowCount(int count) throws SQLException { - this.rowCount = count; - } - - /** - * Only called for insert with generated keys. - */ - public void setGeneratedKey(Object idValue) { - } - - /** - * Perform post execute processing for the CallableSql. - */ - public void postExecute() throws SQLException { - - if (transaction.isLogSummary()) { - String m = "CallableSql label[" + callableSql.getLabel() + "]" + " rows[" + rowCount+ "]" + " bind[" + bindLog + "]"; - transaction.logSummary(m); - } - - // register table modifications with the transaction event - TransactionEventTable tableEvents = callableSql.getTransactionEventTable(); - - if (tableEvents != null && !tableEvents.isEmpty()) { - transaction.getEvent().add(tableEvents); - } else { - transaction.markNotQueryOnly(); - } - - } - - /** - * These need to be set for use with Non-batch execution. Specifically to - * read registered out parameters and potentially handle the - * executeOverride() method. - */ - public void setBound(BindParams bindParam, CallableStatement cstmt) { - this.bindParam = bindParam; - this.cstmt = cstmt; - } - - /** - * Execute the statement in normal non batch mode. - */ - public int executeUpdate() throws SQLException { - - // check to see if the execution has been overridden - // only works in non-batch mode - if (callableSql.executeOverride(cstmt)) { - return -1; - // // been overridden so just return the rowCount - // rowCount = callableSql.getRowCount(); - // return rowCount; - } - - rowCount = cstmt.executeUpdate(); - - // only read in non-batch mode - readOutParams(); - - return rowCount; - } - - private void readOutParams() throws SQLException { - - List list = bindParam.positionedParameters(); - int pos = 0; - - for (int i = 0; i < list.size(); i++) { - pos++; - BindParams.Param param = list.get(i); - if (param.isOutParam()) { - Object outValue = cstmt.getObject(pos); - param.setOutValue(outValue); - } - } - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebean.CallableSql; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.BindParams.Param; +import com.avaje.ebeaninternal.api.SpiCallableSql; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.server.persist.PersistExecute; + +/** + * Persist request specifically for CallableSql. + */ +public final class PersistRequestCallableSql extends PersistRequest { + + private final SpiCallableSql callableSql; + + private int rowCount; + + private String bindLog; + + private CallableStatement cstmt; + + private BindParams bindParam; + + /** + * Create. + */ + public PersistRequestCallableSql(SpiEbeanServer server, + CallableSql cs, SpiTransaction t, PersistExecute persistExecute) { + + super(server, t, persistExecute); + this.type = PersistRequest.Type.CALLABLESQL; + this.callableSql = (SpiCallableSql)cs; + } + + @Override + public int executeOrQueue() { + return executeStatement(); + } + + @Override + public int executeNow() { + return persistExecute.executeSqlCallable(this); + } + + /** + * Return the CallableSql. + */ + public SpiCallableSql getCallableSql() { + return callableSql; + } + + /** + * The the log of bind values. + */ + public void setBindLog(String bindLog) { + this.bindLog = bindLog; + } + + /** + * Note the rowCount of the execution. + */ + public void checkRowCount(int count) throws SQLException { + this.rowCount = count; + } + + /** + * Only called for insert with generated keys. + */ + public void setGeneratedKey(Object idValue) { + } + + /** + * Perform post execute processing for the CallableSql. + */ + public void postExecute() throws SQLException { + + if (transaction.isLogSummary()) { + String m = "CallableSql label[" + callableSql.getLabel() + "]" + " rows[" + rowCount+ "]" + " bind[" + bindLog + "]"; + transaction.logSummary(m); + } + + // register table modifications with the transaction event + TransactionEventTable tableEvents = callableSql.getTransactionEventTable(); + + if (tableEvents != null && !tableEvents.isEmpty()) { + transaction.getEvent().add(tableEvents); + } else { + transaction.markNotQueryOnly(); + } + + } + + /** + * These need to be set for use with Non-batch execution. Specifically to + * read registered out parameters and potentially handle the + * executeOverride() method. + */ + public void setBound(BindParams bindParam, CallableStatement cstmt) { + this.bindParam = bindParam; + this.cstmt = cstmt; + } + + /** + * Execute the statement in normal non batch mode. + */ + public int executeUpdate() throws SQLException { + + // check to see if the execution has been overridden + // only works in non-batch mode + if (callableSql.executeOverride(cstmt)) { + return -1; + // // been overridden so just return the rowCount + // rowCount = callableSql.getRowCount(); + // return rowCount; + } + + rowCount = cstmt.executeUpdate(); + + // only read in non-batch mode + readOutParams(); + + return rowCount; + } + + private void readOutParams() throws SQLException { + + List list = bindParam.positionedParameters(); + int pos = 0; + + for (int i = 0; i < list.size(); i++) { + pos++; + BindParams.Param param = list.get(i); + if (param.isOutParam()) { + Object outValue = cstmt.getObject(pos); + param.setOutValue(outValue); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java index d3364b4b9..ca2c537e9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -1,119 +1,119 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.SQLException; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.SpiUpdate; -import com.avaje.ebeaninternal.api.SpiUpdate.OrmUpdateType; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanManager; -import com.avaje.ebeaninternal.server.persist.PersistExecute; - -/** - * Persist request specifically for CallableSql. - */ -public final class PersistRequestOrmUpdate extends PersistRequest { - - private final BeanDescriptor beanDescriptor; - - private SpiUpdate ormUpdate; - - private int rowCount; - - private String bindLog; - - /** - * Create. - */ - public PersistRequestOrmUpdate(SpiEbeanServer server, BeanManager mgr, SpiUpdate ormUpdate, - SpiTransaction t, PersistExecute persistExecute) { - - super(server, t, persistExecute); - this.beanDescriptor = mgr.getBeanDescriptor(); - this.ormUpdate = ormUpdate; - } - - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - @Override - public int executeNow() { - return persistExecute.executeOrmUpdate(this); - } - - @Override - public int executeOrQueue() { - return executeStatement(); - } - - - /** - * Return the UpdateSql. - */ - public SpiUpdate getOrmUpdate() { - return ormUpdate; - } - - /** - * No concurrency checking so just note the rowCount. - */ - public void checkRowCount(int count) throws SQLException { - this.rowCount = count; - } - - /** - * Always false. - */ - public boolean useGeneratedKeys() { - return false; - } - - /** - * Not called for this type of request. - */ - public void setGeneratedKey(Object idValue) { - } - - /** - * Set the bound values. - */ - public void setBindLog(String bindLog) { - this.bindLog = bindLog; - } - - /** - * Perform post execute processing. - */ - public void postExecute() throws SQLException { - - OrmUpdateType ormUpdateType = ormUpdate.getOrmUpdateType(); - String tableName = ormUpdate.getBaseTable(); - - if (transaction.isLogSummary()) { - String m = ormUpdateType + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]"; - transaction.logSummary(m); - } - - if (ormUpdate.isNotifyCache()) { - - // add the modification info to the TransactionEvent - // this is used to invalidate cached objects etc - switch (ormUpdateType) { - case INSERT: - transaction.getEvent().add(tableName, true, false, false); - break; - case UPDATE: - transaction.getEvent().add(tableName, false, true, false); - break; - case DELETE: - transaction.getEvent().add(tableName, false, false, true); - break; - default: - break; - } - } - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.SQLException; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.SpiUpdate; +import com.avaje.ebeaninternal.api.SpiUpdate.OrmUpdateType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanManager; +import com.avaje.ebeaninternal.server.persist.PersistExecute; + +/** + * Persist request specifically for CallableSql. + */ +public final class PersistRequestOrmUpdate extends PersistRequest { + + private final BeanDescriptor beanDescriptor; + + private SpiUpdate ormUpdate; + + private int rowCount; + + private String bindLog; + + /** + * Create. + */ + public PersistRequestOrmUpdate(SpiEbeanServer server, BeanManager mgr, SpiUpdate ormUpdate, + SpiTransaction t, PersistExecute persistExecute) { + + super(server, t, persistExecute); + this.beanDescriptor = mgr.getBeanDescriptor(); + this.ormUpdate = ormUpdate; + } + + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + @Override + public int executeNow() { + return persistExecute.executeOrmUpdate(this); + } + + @Override + public int executeOrQueue() { + return executeStatement(); + } + + + /** + * Return the UpdateSql. + */ + public SpiUpdate getOrmUpdate() { + return ormUpdate; + } + + /** + * No concurrency checking so just note the rowCount. + */ + public void checkRowCount(int count) throws SQLException { + this.rowCount = count; + } + + /** + * Always false. + */ + public boolean useGeneratedKeys() { + return false; + } + + /** + * Not called for this type of request. + */ + public void setGeneratedKey(Object idValue) { + } + + /** + * Set the bound values. + */ + public void setBindLog(String bindLog) { + this.bindLog = bindLog; + } + + /** + * Perform post execute processing. + */ + public void postExecute() throws SQLException { + + OrmUpdateType ormUpdateType = ormUpdate.getOrmUpdateType(); + String tableName = ormUpdate.getBaseTable(); + + if (transaction.isLogSummary()) { + String m = ormUpdateType + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]"; + transaction.logSummary(m); + } + + if (ormUpdate.isNotifyCache()) { + + // add the modification info to the TransactionEvent + // this is used to invalidate cached objects etc + switch (ormUpdateType) { + case INSERT: + transaction.getEvent().add(tableName, true, false, false); + break; + case UPDATE: + transaction.getEvent().add(tableName, false, true, false); + break; + case DELETE: + transaction.getEvent().add(tableName, false, false, true); + break; + default: + break; + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java index bc0d10947..14610b3ba 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -1,126 +1,126 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.SQLException; - -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiSqlUpdate; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.persist.PersistExecute; - -/** - * Persist request specifically for CallableSql. - */ -public final class PersistRequestUpdateSql extends PersistRequest { - - public enum SqlType { - SQL_UPDATE, SQL_DELETE, SQL_INSERT, SQL_UNKNOWN - }; - - private final SpiSqlUpdate updateSql; - - private int rowCount; - - private String bindLog; - - private SqlType sqlType; - - private String tableName; - - private String description; - - /** - * Create. - */ - public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate updateSql, - SpiTransaction t, PersistExecute persistExecute) { - super(server, t, persistExecute); - this.type = Type.UPDATESQL; - this.updateSql = (SpiSqlUpdate)updateSql; - } - - @Override - public int executeNow() { - return persistExecute.executeSqlUpdate(this); - } - - @Override - public int executeOrQueue() { - return executeStatement(); - } - - /** - * Return the UpdateSql. - */ - public SpiSqlUpdate getUpdateSql() { - return updateSql; - } - - /** - * No concurrency checking so just note the rowCount. - */ - public void checkRowCount(int count) throws SQLException { - this.rowCount = count; - } - - /** - * Always false. - */ - public boolean useGeneratedKeys() { - return false; - } - - /** - * Not called for this type of request. - */ - public void setGeneratedKey(Object idValue) { - } - - /** - * Specify the type of statement executed. Used to automatically register - * with the transaction event. - */ - public void setType(SqlType sqlType, String tableName, String description) { - this.sqlType = sqlType; - this.tableName = tableName; - this.description = description; - } - - /** - * Set the bound values. - */ - public void setBindLog(String bindLog) { - this.bindLog = bindLog; - } - - /** - * Perform post execute processing. - */ - public void postExecute() throws SQLException { - - if (transaction.isLogSummary()) { - String m = description + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]"; - transaction.logSummary(m); - } - - if (updateSql.isAutoTableMod()) { - // add the modification info to the TransactionEvent - // this is used to invalidate cached objects etc - switch (sqlType) { - case SQL_INSERT: - transaction.getEvent().add(tableName, true, false, false); - break; - case SQL_UPDATE: - transaction.getEvent().add(tableName, false, true, false); - break; - case SQL_DELETE: - transaction.getEvent().add(tableName, false, false, true); - break; - - default: - break; - } - } - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.SQLException; + +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiSqlUpdate; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.persist.PersistExecute; + +/** + * Persist request specifically for CallableSql. + */ +public final class PersistRequestUpdateSql extends PersistRequest { + + public enum SqlType { + SQL_UPDATE, SQL_DELETE, SQL_INSERT, SQL_UNKNOWN + }; + + private final SpiSqlUpdate updateSql; + + private int rowCount; + + private String bindLog; + + private SqlType sqlType; + + private String tableName; + + private String description; + + /** + * Create. + */ + public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate updateSql, + SpiTransaction t, PersistExecute persistExecute) { + super(server, t, persistExecute); + this.type = Type.UPDATESQL; + this.updateSql = (SpiSqlUpdate)updateSql; + } + + @Override + public int executeNow() { + return persistExecute.executeSqlUpdate(this); + } + + @Override + public int executeOrQueue() { + return executeStatement(); + } + + /** + * Return the UpdateSql. + */ + public SpiSqlUpdate getUpdateSql() { + return updateSql; + } + + /** + * No concurrency checking so just note the rowCount. + */ + public void checkRowCount(int count) throws SQLException { + this.rowCount = count; + } + + /** + * Always false. + */ + public boolean useGeneratedKeys() { + return false; + } + + /** + * Not called for this type of request. + */ + public void setGeneratedKey(Object idValue) { + } + + /** + * Specify the type of statement executed. Used to automatically register + * with the transaction event. + */ + public void setType(SqlType sqlType, String tableName, String description) { + this.sqlType = sqlType; + this.tableName = tableName; + this.description = description; + } + + /** + * Set the bound values. + */ + public void setBindLog(String bindLog) { + this.bindLog = bindLog; + } + + /** + * Perform post execute processing. + */ + public void postExecute() throws SQLException { + + if (transaction.isLogSummary()) { + String m = description + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]"; + transaction.logSummary(m); + } + + if (updateSql.isAutoTableMod()) { + // add the modification info to the TransactionEvent + // this is used to invalidate cached objects etc + switch (sqlType) { + case SQL_INSERT: + transaction.getEvent().add(tableName, true, false, false); + break; + case SQL_UPDATE: + transaction.getEvent().add(tableName, false, true, false); + break; + case SQL_DELETE: + transaction.getEvent().add(tableName, false, false, true); + break; + + default: + break; + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java index f04a45255..42ffa586d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java @@ -1,93 +1,93 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.Collection; - -import com.avaje.ebean.CallableSql; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.Update; -import com.avaje.ebean.bean.EntityBean; - - -/** - * API for persisting a bean. - */ -public interface Persister { - - /** - * Update the bean. - */ - public void update(EntityBean entityBean, Transaction t); - - /** - * Update the bean specifying deleteMissingChildren. - */ - public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren); - - /** - * Force an Insert using the given bean. - */ - public void insert(EntityBean entityBean, Transaction t); - - /** - * Insert or update the bean depending on its state. - */ - public void save(EntityBean entityBean, Transaction t); - - /** - * Save the associations of a ManyToMany given the owner bean and the - * propertyName of the ManyToMany collection. - */ - public void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t); - - /** - * Save an association (OneToMany, ManyToOne, OneToOne or ManyToMany). - * - * @param parentBean - * the bean that owns the association. - * @param propertyName - * the name of the property to save. - * @param t - * the transaction to use. - */ - public void saveAssociation(EntityBean parentBean, String propertyName, Transaction t); - - /** - * Delete the associations of a ManyToMany given the owner bean and the property name of the ManyToMany. - */ - public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t); - - /** - * Delete a bean given it's type and id value. - *

- * This will also cascade delete one level of children. - *

- */ - public int delete(Class beanType, Object id, Transaction transaction); - - /** - * Delete the bean. - */ - public void delete(EntityBean entityBean, Transaction t); - - /** - * Delete multiple beans given a collection of Id values. - */ - public void deleteMany(Class beanType, Collection ids, Transaction transaction); - - /** - * Execute the Update. - */ - public int executeOrmUpdate(Update update, Transaction t); - - /** - * Execute the UpdateSql. - */ - public int executeSqlUpdate(SqlUpdate update, Transaction t); - - /** - * Execute the CallableSql. - */ - public int executeCallable(CallableSql callable, Transaction t); - -} +package com.avaje.ebeaninternal.server.core; + +import java.util.Collection; + +import com.avaje.ebean.CallableSql; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.Update; +import com.avaje.ebean.bean.EntityBean; + + +/** + * API for persisting a bean. + */ +public interface Persister { + + /** + * Update the bean. + */ + public void update(EntityBean entityBean, Transaction t); + + /** + * Update the bean specifying deleteMissingChildren. + */ + public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren); + + /** + * Force an Insert using the given bean. + */ + public void insert(EntityBean entityBean, Transaction t); + + /** + * Insert or update the bean depending on its state. + */ + public void save(EntityBean entityBean, Transaction t); + + /** + * Save the associations of a ManyToMany given the owner bean and the + * propertyName of the ManyToMany collection. + */ + public void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t); + + /** + * Save an association (OneToMany, ManyToOne, OneToOne or ManyToMany). + * + * @param parentBean + * the bean that owns the association. + * @param propertyName + * the name of the property to save. + * @param t + * the transaction to use. + */ + public void saveAssociation(EntityBean parentBean, String propertyName, Transaction t); + + /** + * Delete the associations of a ManyToMany given the owner bean and the property name of the ManyToMany. + */ + public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t); + + /** + * Delete a bean given it's type and id value. + *

+ * This will also cascade delete one level of children. + *

+ */ + public int delete(Class beanType, Object id, Transaction transaction); + + /** + * Delete the bean. + */ + public void delete(EntityBean entityBean, Transaction t); + + /** + * Delete multiple beans given a collection of Id values. + */ + public void deleteMany(Class beanType, Collection ids, Transaction transaction); + + /** + * Execute the Update. + */ + public int executeOrmUpdate(Update update, Transaction t); + + /** + * Execute the UpdateSql. + */ + public int executeSqlUpdate(SqlUpdate update, Transaction t); + + /** + * Execute the CallableSql. + */ + public int executeCallable(CallableSql callable, Transaction t); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java b/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java index 124845f46..f796a3e74 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java @@ -1,17 +1,17 @@ -package com.avaje.ebeaninternal.server.core; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -/** - * If Oracle supported the JDBC api fully this would not be required. - */ -public interface PstmtBatch { - - public void setBatchSize(PreparedStatement pstmt, int batchSize); - - public void addBatch(PreparedStatement pstmt) throws SQLException; - - public int executeBatch(PreparedStatement pstmt, int expectedRows, String sql, boolean occCheck) throws SQLException; - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * If Oracle supported the JDBC api fully this would not be required. + */ +public interface PstmtBatch { + + public void setBatchSize(PreparedStatement pstmt, int batchSize); + + public void addBatch(PreparedStatement pstmt) throws SQLException; + + public int executeBatch(PreparedStatement pstmt, int expectedRows, String sql, boolean occCheck) throws SQLException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java b/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java index 6a1c1047e..bd5622718 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java @@ -1,215 +1,215 @@ -package com.avaje.ebeaninternal.server.core; - - -/** - * Helper for performing a 'refresh' on an Entity bean. - *

- * Note that this does not 'refresh' any OnetoMany or ManyToMany properties. It - * refreshes all the other properties though. - *

- */ -public class RefreshHelp { -// -// /** -// * Helper for debug of lazy loading. -// */ -// private final DebugLazyLoad debugLazyLoad; -// -// private final MAdminLoggingMBean logControl; -// -// public RefreshHelp(MAdminLoggingMBean logControl, boolean debugLazyLoad){ -// this.logControl = logControl; -// this.debugLazyLoad = new DebugLazyLoad(debugLazyLoad); -// } -// -// /** -// * Refresh the bean from property values in dbBean. -// */ -// public void refresh(Object o, Object dbBean, BeanDescriptor desc, EntityBeanIntercept ebi, Object id, boolean isLazyLoad) { -// -// Object originalOldValues = null; -// boolean setOriginalOldValues = false; -// -// // set of properties to exclude from the refresh because it is -// // not a refresh but rather a lazyLoading event. -// Set excludes = null; -// -// // turn off intercepting so lazy loading is -// // not invoked when populating the bean -// // with PropertyChangeSupport -// ebi.setIntercepting(false); -// -// boolean readOnly = ebi.isReadOnly(); -// boolean sharedInstance = ebi.isSharedInstance(); -// -// if (isLazyLoad){ -// excludes = ebi.getLoadedProps(); -// if (excludes != null){ -// // lazy loading a "Partial Object"... which already -// // contains some properties and perhaps some oldValues -// // and these will need to be maintained... -// originalOldValues = ebi.getOldValues(); -// setOriginalOldValues = originalOldValues != null; -// } -// -// if (logControl.isDebugLazyLoad()){ -// debug(desc, ebi, id, excludes); -// } -// } -// -// -// BeanProperty[] props = desc.propertiesBaseScalar(); -// for (int i = 0; i < props.length; i++) { -// BeanProperty prop = props[i]; -// if (excludes != null && excludes.contains(prop.getName())){ -// // ignore this property (partial bean lazy loading) -// -// } else { -// Object dbVal = prop.getValue(dbBean); -// if (isLazyLoad) { -// prop.setValue(o, dbVal); -// } else { -// prop.setValueIntercept(o, dbVal); -// } -// if (setOriginalOldValues){ -// // maintain original oldValues for partially loaded bean -// prop.setValue(originalOldValues, dbVal); -// } -// } -// } -// -// BeanPropertyAssocOne[] ones = desc.propertiesOne(); -// for (int i = 0; i < ones.length; i++) { -// BeanProperty prop = ones[i]; -// if (excludes != null && excludes.contains(prop.getName())){ -// // ignore this property (partial bean lazy loading) -// -// } else { -// Object dbVal = prop.getValue(dbBean); -// if (isLazyLoad){ -// prop.setValue(o, dbVal); -// } else { -// prop.setValueIntercept(o, dbVal); -// } -// if (setOriginalOldValues){ -// // maintain original oldValues for partially loaded bean -// prop.setValue(originalOldValues, dbVal); -// } -// if (dbVal != null){ -// if (sharedInstance){ -// // propagate sharedInstance status to associated beans -// ((EntityBean)dbVal)._ebean_getIntercept().setSharedInstance(); -// } else if (readOnly) { -// // propagate readOnly status to associated beans -// ((EntityBean)dbVal)._ebean_getIntercept().setReadOnly(true); -// } -// } -// -// } -// } -// -// refreshEmbedded(o, dbBean, desc, excludes, readOnly); -// -// // set a lazy loading many proxy if required -// BeanPropertyAssocMany[] manys = desc.propertiesMany(); -// for (int i = 0; i < manys.length; i++) { -// BeanPropertyAssocMany prop = manys[i]; -// if (excludes != null && excludes.contains(prop.getName())){ -// // the many already existed on the bean -// -// } else { -// // set a lazy loading proxy -// prop.createReference(o, null, readOnly, sharedInstance); -// } -// } -// -// // the refreshed/lazy loaded bean is always fully -// // populated so set loadedProps to null -// ebi.setLoadedProps(null); -// -// -// // reset the loaded status -// ebi.setLoaded(); -// } -// -// /** -// * Refresh the Embedded beans. -// */ -// private void refreshEmbedded(Object o, Object dbBean, BeanDescriptor desc, Set excludes, boolean propagateReadOnly) { -// -// BeanPropertyAssocOne[] embeds = desc.propertiesEmbedded(); -// for (int i = 0; i < embeds.length; i++) { -// BeanPropertyAssocOne prop = embeds[i]; -// if (excludes != null && excludes.contains(prop.getName())){ -// // ignore this property -// } else { -// // the original embedded bean -// Object oEmb = prop.getValue(o); -// -// // the new one from the database -// Object dbEmb = prop.getValue(dbBean); -// -// if (oEmb == null){ -// // original embedded bean was null -// // so just replace the entire embedded bean -// prop.setValueIntercept(o, dbEmb); -// if (propagateReadOnly && dbEmb != null){ -// // propagate readOnly status to embedded beans -// ((EntityBean)dbEmb)._ebean_getIntercept().setReadOnly(true); -// } -// -// } else { -// // refresh each property of the original -// // embedded bean -// if (oEmb instanceof EntityBean){ -// // turn off interception to stop invoking lazy loading -// // but allow PropertyChangeSupport -// ((EntityBean) oEmb)._ebean_getIntercept().setIntercepting(false); -// } -// -// BeanProperty[] props = prop.getProperties(); -// for (int j = 0; j < props.length; j++) { -// Object v = props[j].getValue(dbEmb); -// props[j].setValueIntercept(oEmb, v); -// } -// -// // No longer calling setLoaded() on embedded bean -// // as the EntityBean itself -// // .. calls setEmbeddedLoaded() on each of -// // .. its embedded beans itself. -// } -// } -// } -// } -// -// -// /** -// * Output some debug to describe the lazy loading event. -// */ -// private void debug(BeanDescriptor desc, EntityBeanIntercept ebi, Object id, Set excludes) { -// -// -// Class beanType = desc.getBeanType(); -// -// StackTraceElement cause = debugLazyLoad.getStackTraceElement(beanType); -// -// String lazyLoadProperty = ebi.getLazyLoadProperty(); -// String msg = "debug.lazyLoad ["+desc+"] id["+id+"] lazyLoadProperty["+lazyLoadProperty+"]"; -// if (excludes != null){ -// msg += " partialProps"+excludes; -// } -// if (cause != null){ -// String causeLine = cause.toString(); -// if (causeLine.indexOf(".groovy:") > -1){ -// // eclipse console does not like finding groovy source at the moment -// causeLine = StringHelper.replaceString(causeLine, ".groovy:", ".groovy :"); -// } -// msg += " at: "+causeLine; -// } -// System.err.println(msg); -// } -// -// -// - -} +package com.avaje.ebeaninternal.server.core; + + +/** + * Helper for performing a 'refresh' on an Entity bean. + *

+ * Note that this does not 'refresh' any OnetoMany or ManyToMany properties. It + * refreshes all the other properties though. + *

+ */ +public class RefreshHelp { +// +// /** +// * Helper for debug of lazy loading. +// */ +// private final DebugLazyLoad debugLazyLoad; +// +// private final MAdminLoggingMBean logControl; +// +// public RefreshHelp(MAdminLoggingMBean logControl, boolean debugLazyLoad){ +// this.logControl = logControl; +// this.debugLazyLoad = new DebugLazyLoad(debugLazyLoad); +// } +// +// /** +// * Refresh the bean from property values in dbBean. +// */ +// public void refresh(Object o, Object dbBean, BeanDescriptor desc, EntityBeanIntercept ebi, Object id, boolean isLazyLoad) { +// +// Object originalOldValues = null; +// boolean setOriginalOldValues = false; +// +// // set of properties to exclude from the refresh because it is +// // not a refresh but rather a lazyLoading event. +// Set excludes = null; +// +// // turn off intercepting so lazy loading is +// // not invoked when populating the bean +// // with PropertyChangeSupport +// ebi.setIntercepting(false); +// +// boolean readOnly = ebi.isReadOnly(); +// boolean sharedInstance = ebi.isSharedInstance(); +// +// if (isLazyLoad){ +// excludes = ebi.getLoadedProps(); +// if (excludes != null){ +// // lazy loading a "Partial Object"... which already +// // contains some properties and perhaps some oldValues +// // and these will need to be maintained... +// originalOldValues = ebi.getOldValues(); +// setOriginalOldValues = originalOldValues != null; +// } +// +// if (logControl.isDebugLazyLoad()){ +// debug(desc, ebi, id, excludes); +// } +// } +// +// +// BeanProperty[] props = desc.propertiesBaseScalar(); +// for (int i = 0; i < props.length; i++) { +// BeanProperty prop = props[i]; +// if (excludes != null && excludes.contains(prop.getName())){ +// // ignore this property (partial bean lazy loading) +// +// } else { +// Object dbVal = prop.getValue(dbBean); +// if (isLazyLoad) { +// prop.setValue(o, dbVal); +// } else { +// prop.setValueIntercept(o, dbVal); +// } +// if (setOriginalOldValues){ +// // maintain original oldValues for partially loaded bean +// prop.setValue(originalOldValues, dbVal); +// } +// } +// } +// +// BeanPropertyAssocOne[] ones = desc.propertiesOne(); +// for (int i = 0; i < ones.length; i++) { +// BeanProperty prop = ones[i]; +// if (excludes != null && excludes.contains(prop.getName())){ +// // ignore this property (partial bean lazy loading) +// +// } else { +// Object dbVal = prop.getValue(dbBean); +// if (isLazyLoad){ +// prop.setValue(o, dbVal); +// } else { +// prop.setValueIntercept(o, dbVal); +// } +// if (setOriginalOldValues){ +// // maintain original oldValues for partially loaded bean +// prop.setValue(originalOldValues, dbVal); +// } +// if (dbVal != null){ +// if (sharedInstance){ +// // propagate sharedInstance status to associated beans +// ((EntityBean)dbVal)._ebean_getIntercept().setSharedInstance(); +// } else if (readOnly) { +// // propagate readOnly status to associated beans +// ((EntityBean)dbVal)._ebean_getIntercept().setReadOnly(true); +// } +// } +// +// } +// } +// +// refreshEmbedded(o, dbBean, desc, excludes, readOnly); +// +// // set a lazy loading many proxy if required +// BeanPropertyAssocMany[] manys = desc.propertiesMany(); +// for (int i = 0; i < manys.length; i++) { +// BeanPropertyAssocMany prop = manys[i]; +// if (excludes != null && excludes.contains(prop.getName())){ +// // the many already existed on the bean +// +// } else { +// // set a lazy loading proxy +// prop.createReference(o, null, readOnly, sharedInstance); +// } +// } +// +// // the refreshed/lazy loaded bean is always fully +// // populated so set loadedProps to null +// ebi.setLoadedProps(null); +// +// +// // reset the loaded status +// ebi.setLoaded(); +// } +// +// /** +// * Refresh the Embedded beans. +// */ +// private void refreshEmbedded(Object o, Object dbBean, BeanDescriptor desc, Set excludes, boolean propagateReadOnly) { +// +// BeanPropertyAssocOne[] embeds = desc.propertiesEmbedded(); +// for (int i = 0; i < embeds.length; i++) { +// BeanPropertyAssocOne prop = embeds[i]; +// if (excludes != null && excludes.contains(prop.getName())){ +// // ignore this property +// } else { +// // the original embedded bean +// Object oEmb = prop.getValue(o); +// +// // the new one from the database +// Object dbEmb = prop.getValue(dbBean); +// +// if (oEmb == null){ +// // original embedded bean was null +// // so just replace the entire embedded bean +// prop.setValueIntercept(o, dbEmb); +// if (propagateReadOnly && dbEmb != null){ +// // propagate readOnly status to embedded beans +// ((EntityBean)dbEmb)._ebean_getIntercept().setReadOnly(true); +// } +// +// } else { +// // refresh each property of the original +// // embedded bean +// if (oEmb instanceof EntityBean){ +// // turn off interception to stop invoking lazy loading +// // but allow PropertyChangeSupport +// ((EntityBean) oEmb)._ebean_getIntercept().setIntercepting(false); +// } +// +// BeanProperty[] props = prop.getProperties(); +// for (int j = 0; j < props.length; j++) { +// Object v = props[j].getValue(dbEmb); +// props[j].setValueIntercept(oEmb, v); +// } +// +// // No longer calling setLoaded() on embedded bean +// // as the EntityBean itself +// // .. calls setEmbeddedLoaded() on each of +// // .. its embedded beans itself. +// } +// } +// } +// } +// +// +// /** +// * Output some debug to describe the lazy loading event. +// */ +// private void debug(BeanDescriptor desc, EntityBeanIntercept ebi, Object id, Set excludes) { +// +// +// Class beanType = desc.getBeanType(); +// +// StackTraceElement cause = debugLazyLoad.getStackTraceElement(beanType); +// +// String lazyLoadProperty = ebi.getLazyLoadProperty(); +// String msg = "debug.lazyLoad ["+desc+"] id["+id+"] lazyLoadProperty["+lazyLoadProperty+"]"; +// if (excludes != null){ +// msg += " partialProps"+excludes; +// } +// if (cause != null){ +// String causeLine = cause.toString(); +// if (causeLine.indexOf(".groovy:") > -1){ +// // eclipse console does not like finding groovy source at the moment +// causeLine = StringHelper.replaceString(causeLine, ".groovy:", ".groovy :"); +// } +// msg += " at: "+causeLine; +// } +// System.err.println(msg); +// } +// +// +// + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/RelationalQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/RelationalQueryRequest.java index 9af3cc758..63cca02ba 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/RelationalQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/RelationalQueryRequest.java @@ -1,114 +1,114 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.SqlQuery; -import com.avaje.ebean.SqlRow; -import com.avaje.ebean.Transaction; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiSqlQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * Wraps the objects involved in executing a SqlQuery. - */ -public final class RelationalQueryRequest { - - private final SpiSqlQuery query; - - private final RelationalQueryEngine queryEngine; - - private final SpiEbeanServer ebeanServer; - - private SpiTransaction trans; - - private boolean createdTransaction; - - private SpiQuery.Type queryType; - - /** - * Create the BeanFindRequest. - */ - public RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) { - this.ebeanServer = server; - this.queryEngine = engine; - this.query = (SpiSqlQuery) q; - this.trans = (SpiTransaction) t; - } - - /** - * Create a transaction if none currently exists. - */ - public void initTransIfRequired() { - if (trans == null) { - trans = ebeanServer.getCurrentServerTransaction(); - if (trans == null || !trans.isActive()) { - // create a local readOnly transaction - trans = ebeanServer.createServerTransaction(false, -1); - createdTransaction = true; - } - } - } - - /** - * End the transaction if it was locally created. - */ - public void endTransIfRequired() { - if (createdTransaction) { - trans.endQueryOnly(); - } - } - - @SuppressWarnings("unchecked") - public List findList() { - queryType = SpiQuery.Type.LIST; - return (List) queryEngine.findMany(this); - } - - @SuppressWarnings("unchecked") - public Set findSet() { - queryType = SpiQuery.Type.SET; - return (Set) queryEngine.findMany(this); - } - - @SuppressWarnings("unchecked") - public Map findMap() { - queryType = SpiQuery.Type.MAP; - return (Map) queryEngine.findMany(this); - } - - /** - * Return the find that is to be performed. - */ - public SpiSqlQuery getQuery() { - return query; - } - - /** - * Return the type (List, Set or Map) that this fetch returns. - */ - public SpiQuery.Type getQueryType() { - return queryType; - } - - public EbeanServer getEbeanServer() { - return ebeanServer; - } - - public SpiTransaction getTransaction() { - return trans; - } - - public boolean isLogSql() { - return trans.isLogSql(); - } - - public boolean isLogSummary() { - return trans.isLogSummary(); - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.SqlQuery; +import com.avaje.ebean.SqlRow; +import com.avaje.ebean.Transaction; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiSqlQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * Wraps the objects involved in executing a SqlQuery. + */ +public final class RelationalQueryRequest { + + private final SpiSqlQuery query; + + private final RelationalQueryEngine queryEngine; + + private final SpiEbeanServer ebeanServer; + + private SpiTransaction trans; + + private boolean createdTransaction; + + private SpiQuery.Type queryType; + + /** + * Create the BeanFindRequest. + */ + public RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) { + this.ebeanServer = server; + this.queryEngine = engine; + this.query = (SpiSqlQuery) q; + this.trans = (SpiTransaction) t; + } + + /** + * Create a transaction if none currently exists. + */ + public void initTransIfRequired() { + if (trans == null) { + trans = ebeanServer.getCurrentServerTransaction(); + if (trans == null || !trans.isActive()) { + // create a local readOnly transaction + trans = ebeanServer.createServerTransaction(false, -1); + createdTransaction = true; + } + } + } + + /** + * End the transaction if it was locally created. + */ + public void endTransIfRequired() { + if (createdTransaction) { + trans.endQueryOnly(); + } + } + + @SuppressWarnings("unchecked") + public List findList() { + queryType = SpiQuery.Type.LIST; + return (List) queryEngine.findMany(this); + } + + @SuppressWarnings("unchecked") + public Set findSet() { + queryType = SpiQuery.Type.SET; + return (Set) queryEngine.findMany(this); + } + + @SuppressWarnings("unchecked") + public Map findMap() { + queryType = SpiQuery.Type.MAP; + return (Map) queryEngine.findMany(this); + } + + /** + * Return the find that is to be performed. + */ + public SpiSqlQuery getQuery() { + return query; + } + + /** + * Return the type (List, Set or Map) that this fetch returns. + */ + public SpiQuery.Type getQueryType() { + return queryType; + } + + public EbeanServer getEbeanServer() { + return ebeanServer; + } + + public SpiTransaction getTransaction() { + return trans; + } + + public boolean isLogSql() { + return trans.isLogSql(); + } + + public boolean isLogSummary() { + return trans.isLogSummary(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/ServletContextListener.java b/src/main/java/com/avaje/ebeaninternal/server/core/ServletContextListener.java index 269336722..a00613e1e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/ServletContextListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/ServletContextListener.java @@ -1,31 +1,31 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebeaninternal.server.lib.ShutdownManager; - -import javax.servlet.ServletContextEvent; - -/** - * Listens for webserver server starting and stopping events. - * - *

- * Register this listener in the web.xml configuration file. This will listen - * for startup and shutdown events. - *

- */ -public class ServletContextListener implements javax.servlet.ServletContextListener { - - /** - * The servlet container is stopping. - */ - public void contextDestroyed(ServletContextEvent event) { - ShutdownManager.shutdown(); - } - - /** - * Do nothing on startup. - */ - public void contextInitialized(ServletContextEvent event) { - - } - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebeaninternal.server.lib.ShutdownManager; + +import javax.servlet.ServletContextEvent; + +/** + * Listens for webserver server starting and stopping events. + * + *

+ * Register this listener in the web.xml configuration file. This will listen + * for startup and shutdown events. + *

+ */ +public class ServletContextListener implements javax.servlet.ServletContextListener { + + /** + * The servlet container is stopping. + */ + public void contextDestroyed(ServletContextEvent event) { + ShutdownManager.shutdown(); + } + + /** + * Do nothing on startup. + */ + public void contextInitialized(ServletContextEvent event) { + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java index 52c5e1508..28878bd27 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java @@ -1,115 +1,115 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.QueryEachConsumer; -import com.avaje.ebean.QueryEachWhileConsumer; -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.QueryResultVisitor; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Defines the ORM query request api. - */ -public interface SpiOrmQueryRequest { - - /** - * Return the query. - */ - public SpiQuery getQuery(); - - /** - * Return the associated BeanDescriptor. - */ - public BeanDescriptor getBeanDescriptor(); - - /** - * This will create a local (readOnly) transaction if no current transaction - * exists. - *

- * A transaction may have been passed in explicitly or currently be active - * in the thread local. If not, then a readOnly transaction is created to - * execute this query. - *

- */ - public void initTransIfRequired(); - - /** - * Will end a locally created transaction. - *

- * It ends the transaction by using a rollback() as the transaction is known - * to be readOnly. - *

- */ - public void endTransIfRequired(); - - /** - * Execute the query as findById. - */ - public Object findId(); - - /** - * Execute the find row count query. - */ - public int findRowCount(); - - /** - * Execute the find ids query. - */ - public List findIds(); - - /** - * Execute the find returning a QueryIterator and visitor pattern. - */ - public void findVisit(QueryResultVisitor visitor); - - /** - * Execute the find returning a QueryIterator and visitor pattern. - */ - public void findEach(QueryEachConsumer consumer); - - /** - * Execute the find returning a QueryIterator and visitor pattern. - */ - public void findEachWhile(QueryEachWhileConsumer consumer); - - /** - * Execute the find returning a QueryIterator. - */ - public QueryIterator findIterate(); - - /** - * Execute the query as findList. - */ - public List findList(); - - /** - * Execute the query as findSet. - */ - public Set findSet(); - - /** - * Execute the query as findMap. - */ - public Map findMap(); - - /** - * Try to get the object out of the persistence context. - */ - //public T getFromPersistenceContextOrCache(); - - /** - * Try to get the query result from the query cache. - */ - public BeanCollection getFromQueryCache(); - - /** - * Return the Database platform like clause. - */ - public String getDBLikeClause(); - +package com.avaje.ebeaninternal.server.core; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.avaje.ebean.QueryEachConsumer; +import com.avaje.ebean.QueryEachWhileConsumer; +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.QueryResultVisitor; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Defines the ORM query request api. + */ +public interface SpiOrmQueryRequest { + + /** + * Return the query. + */ + public SpiQuery getQuery(); + + /** + * Return the associated BeanDescriptor. + */ + public BeanDescriptor getBeanDescriptor(); + + /** + * This will create a local (readOnly) transaction if no current transaction + * exists. + *

+ * A transaction may have been passed in explicitly or currently be active + * in the thread local. If not, then a readOnly transaction is created to + * execute this query. + *

+ */ + public void initTransIfRequired(); + + /** + * Will end a locally created transaction. + *

+ * It ends the transaction by using a rollback() as the transaction is known + * to be readOnly. + *

+ */ + public void endTransIfRequired(); + + /** + * Execute the query as findById. + */ + public Object findId(); + + /** + * Execute the find row count query. + */ + public int findRowCount(); + + /** + * Execute the find ids query. + */ + public List findIds(); + + /** + * Execute the find returning a QueryIterator and visitor pattern. + */ + public void findVisit(QueryResultVisitor visitor); + + /** + * Execute the find returning a QueryIterator and visitor pattern. + */ + public void findEach(QueryEachConsumer consumer); + + /** + * Execute the find returning a QueryIterator and visitor pattern. + */ + public void findEachWhile(QueryEachWhileConsumer consumer); + + /** + * Execute the find returning a QueryIterator. + */ + public QueryIterator findIterate(); + + /** + * Execute the query as findList. + */ + public List findList(); + + /** + * Execute the query as findSet. + */ + public Set findSet(); + + /** + * Execute the query as findMap. + */ + public Map findMap(); + + /** + * Try to get the object out of the persistence context. + */ + //public T getFromPersistenceContextOrCache(); + + /** + * Try to get the query result from the query cache. + */ + public BeanCollection getFromQueryCache(); + + /** + * Return the Database platform like clause. + */ + public String getDBLikeClause(); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java index d1246ee4e..7f97c0ef3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java @@ -1,64 +1,64 @@ -package com.avaje.ebeaninternal.server.core; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.lib.util.Dnode; - -/** - * Holds the orm.xml and ebean-orm.xml deployment information. - * - * @author rbygrave - */ -public class XmlConfig { - - private final List ebeanOrmXml; - private final List ormXml; - private final List allXml; - - public XmlConfig(List ormXml, List ebeanOrmXml){ - this.ormXml = ormXml; - this.ebeanOrmXml = ebeanOrmXml; - this.allXml = new ArrayList(ormXml.size() + ebeanOrmXml.size()); - allXml.addAll(ormXml); - allXml.addAll(ebeanOrmXml); - } - - public List getEbeanOrmXml() { - return ebeanOrmXml; - } - - public List getOrmXml() { - return ormXml; - } - - public List find(List entityXml, String element) { - ArrayList hits = new ArrayList(); - for (int i = 0; i < entityXml.size(); i++) { - hits.addAll(entityXml.get(i).findAll(element, 1)); - } - return hits; - } - - /** - * Find the deployment xml for a given entity. - *

- * This searches all the orm.xml and ebean-orm.xml files. - *

- */ - public List findEntityXml(String className) { - - ArrayList hits = new ArrayList(2); - - for (Dnode ormXml : allXml) { - Dnode entityMappings = ormXml.find("entity-mappings"); - - List entities = entityMappings.findAll("entity", "class", className, 1); - if (entities.size() == 1) { - hits.add(entities.get(0)); - } - } - - return hits; - } -} +package com.avaje.ebeaninternal.server.core; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.lib.util.Dnode; + +/** + * Holds the orm.xml and ebean-orm.xml deployment information. + * + * @author rbygrave + */ +public class XmlConfig { + + private final List ebeanOrmXml; + private final List ormXml; + private final List allXml; + + public XmlConfig(List ormXml, List ebeanOrmXml){ + this.ormXml = ormXml; + this.ebeanOrmXml = ebeanOrmXml; + this.allXml = new ArrayList(ormXml.size() + ebeanOrmXml.size()); + allXml.addAll(ormXml); + allXml.addAll(ebeanOrmXml); + } + + public List getEbeanOrmXml() { + return ebeanOrmXml; + } + + public List getOrmXml() { + return ormXml; + } + + public List find(List entityXml, String element) { + ArrayList hits = new ArrayList(); + for (int i = 0; i < entityXml.size(); i++) { + hits.addAll(entityXml.get(i).findAll(element, 1)); + } + return hits; + } + + /** + * Find the deployment xml for a given entity. + *

+ * This searches all the orm.xml and ebean-orm.xml files. + *

+ */ + public List findEntityXml(String className) { + + ArrayList hits = new ArrayList(2); + + for (Dnode ormXml : allXml) { + Dnode entityMappings = ormXml.find("entity-mappings"); + + List entities = entityMappings.findAll("entity", "class", className, 1); + if (entities.size() == 1) { + hits.add(entities.get(0)); + } + } + + return hits; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java index be1ba7be0..b5e3006f1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java @@ -1,71 +1,71 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebeaninternal.server.lib.util.Dnode; -import com.avaje.ebeaninternal.server.lib.util.DnodeReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - * Used to read the orm.xml and ebean-orm.xml configuration files. - * - * @author rbygrave - * @author Richard Vowles - http://plus.google.com/RichardVowles - */ -public class XmlConfigLoader { - - private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class); - - private final ClassLoader classLoader; - - public XmlConfigLoader(ClassLoader classLoader) { - - if (classLoader == null) { - classLoader = getClass().getClassLoader(); - } - - this.classLoader = classLoader; - } - - public XmlConfig load() { - List ormXml = search("META-INF/orm.xml"); - List ebeanOrmXml = search("META-INF/ebean-orm.xml"); - - return new XmlConfig(ormXml, ebeanOrmXml); - } - - public List search(String resourceName) { - ArrayList xmlList = new ArrayList(); - - try { - Enumeration resources = classLoader.getResources(resourceName); - - while (resources.hasMoreElements()) { - URL url = resources.nextElement(); - - InputStream is = url.openStream(); - processInputStream(xmlList, is); - is.close(); - } - } catch (IOException e) { - logger.error("Unable to find resources {}", resourceName); - } - - return xmlList; - } - - private void processInputStream(ArrayList xmlList, InputStream is) throws IOException { - - DnodeReader reader = new DnodeReader(); - Dnode xmlDoc = reader.parseXml(is); - is.close(); - - xmlList.add(xmlDoc); - } -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebeaninternal.server.lib.util.Dnode; +import com.avaje.ebeaninternal.server.lib.util.DnodeReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * Used to read the orm.xml and ebean-orm.xml configuration files. + * + * @author rbygrave + * @author Richard Vowles - http://plus.google.com/RichardVowles + */ +public class XmlConfigLoader { + + private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class); + + private final ClassLoader classLoader; + + public XmlConfigLoader(ClassLoader classLoader) { + + if (classLoader == null) { + classLoader = getClass().getClassLoader(); + } + + this.classLoader = classLoader; + } + + public XmlConfig load() { + List ormXml = search("META-INF/orm.xml"); + List ebeanOrmXml = search("META-INF/ebean-orm.xml"); + + return new XmlConfig(ormXml, ebeanOrmXml); + } + + public List search(String resourceName) { + ArrayList xmlList = new ArrayList(); + + try { + Enumeration resources = classLoader.getResources(resourceName); + + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + + InputStream is = url.openStream(); + processInputStream(xmlList, is); + is.close(); + } + } catch (IOException e) { + logger.error("Unable to find resources {}", resourceName); + } + + return xmlList; + } + + private void processInputStream(ArrayList xmlList, InputStream is) throws IOException { + + DnodeReader reader = new DnodeReader(); + Dnode xmlDoc = reader.parseXml(is); + is.close(); + + xmlList.add(xmlDoc); + } +}