Change license to Apache2 and reformat

This commit is contained in:
rbygrave
2012-09-15 00:00:08 +12:00
parent 96ce4c0ddf
commit 7aae897def
560 changed files with 73292 additions and 85907 deletions
@@ -1,263 +1,244 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.autofetch;
import java.util.Iterator;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.NodeUsageListener;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.AutofetchMode;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Collects and manages the the profile information.
* <p>
* The profile information is periodically converted into "tuned query details" -
* which is used to automatically tune the queries that use autoFetch.
* </p>
* <p>
* The "tuned query details" effectively are part of the query that has the
* select() and join() information (but not the where clause, order by, limits
* etc). These are applied to the query when tuneQuery() is called.
* </p>
*/
public interface AutoFetchManager extends NodeUsageListener {
/**
* Set the owning ebean server.
*/
public void setOwner(SpiEbeanServer server, ServerConfig serverConfig);
/**
* Clear the query execution statistics.
*/
public void clearQueryStatistics();
/**
* Clear all the tuned query info.
* <p>
* Should only need do this for testing and playing around.
* </p>
*/
public int clearTunedQueryInfo();
/**
* Clear all the profiling information.
* <p>
* This means the profiling information will need to be re-gathered.
* </p>
* <p>
* Should only need do this for testing and playing around.
* </p>
*/
public int clearProfilingInfo();
/**
* On shutdown fire garbage collection and collect statistics. Note that
* usually we add a little delay (100 milliseconds) to give the garbage
* collector plenty of time to do its thing and collect the profile
* information.
*/
public void shutdown();
/**
* Return the current tuned fetch information for a given queryPoint key.
*/
public TunedQueryInfo getTunedQueryInfo(String queryPointKey);
/**
* Return the current Statistics for a given queryPoint key.
*/
public Statistics getStatistics(String queryPointKey);
/**
* Iterate the tuned fetch info.
* <p>
* This should be a read only iteration.
* </p>
*/
public Iterator<TunedQueryInfo> iterateTunedQueryInfo();
/**
* Iterate the node usage statistics.
* <p>
* This should be a read only iteration.
* </p>
*/
public Iterator<Statistics> iterateStatistics();
/**
* Return true if profiling is enabled.
*/
public boolean isProfiling();
/**
* Set to true to enable profiling.
* <p>
* We rely on garbage collection to collect the profiling information. This
* means there is a unknown delay between when a query is executed and when
* we actually collect the usage profile information.
* </p>
* <p>
* Due to this garbage collection delay, when turning off profiling while
* the application is running you should consider calling
* collectUsageViaGC() <em>BEFORE</em> setProfiling(false). This hints to
* the JVM to perform garbage collection, and hopefully collects the
* profiling information.
* </p>
*/
public void setProfiling(boolean enable);
/**
* Return true if automatic query tuning is enabled.
*/
public boolean isQueryTuning();
/**
* Set to true to enable automatic query tuning.
*/
public void setQueryTuning(boolean enable);
/**
* This controls whether autoFetch is used when it has not been explicitly
* set on a query via {@link Query#setAutoFetch(boolean)}.
*/
public AutofetchMode getMode();
/**
* Set the auto fetch mode used when a query has not had
* {@link Query#setAutoFetch(boolean)}.
*/
public void setMode(AutofetchMode Mode);
/**
* Return the profiling rate (int between 0 and 100).
*/
public double getProfilingRate();
/**
* Set the profiling rate (int between 0 and 100).
*/
public void setProfilingRate(double rate);
/**
* Return the max number of queries profiled (per query point).
* <p>
* The number of queries profiled is collected per query point. Once a query
* point has profiled this number of queries it does not profile any more.
* </p>
*/
public int getProfilingBase();
/**
* Set a max number of queries to profile per query point.
* <p>
* This number should provide a level of confidence that no more profiling
* is required for this query point.
* </p>
*/
public void setProfilingBase(int profilingMax);
/**
* Return the minimum number of queries profiled before autoFetch will start
* automatically tuning the queries.
* <p>
* This could be one which means start autoFetch tuning after the first
* profiling information is collected.
* </p>
*/
public int getProfilingMin();
/**
* Set the minimum number of queries profiled per query point before
* autoFetch will automatically tune the queries.
* <p>
* Increasing this number will mean more profiling is collected before
* autoFetch starts tuning the query.
* </p>
*/
public void setProfilingMin(int autoFetchMinThreshold);
/**
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
* fires this will gather the usage profiling information.
*/
public String collectUsageViaGC(long waitMillis);
/**
* This will take the current profiling information and update the "tuned
* query detail".
* <p>
* This is done periodically and can also be manually invoked.
* </p>
* <p>
* This returns a string summary of the updates that occurred.
* </p>
*/
public String updateTunedQueryInfo();
/**
* Called when a query thinks it should be automatically tuned by autoFetch.
* <p>
* This internally checks that autoFetch is enabled, there is a "tuned query
* detail" to tune the query with and that the autoFetchMinThreshold has
* been reached.
* </p>
* <p>
* This will also determine if the query should be profiled.
* </p>
*/
public boolean tuneQuery(SpiQuery<?> query);
/**
* Collect query profiling information.
* <p>
* This is for the original query as well as any subsequent lazy loading
* queries that are required as the object graph is traversed.
* </p>
*
* @param node
* the node path in the object graph.
* @param beans
* the number of beans loaded by the query.
* @param micros
* the query executing time in microseconds
*/
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
/**
* Return the number of queries tuned by AutoFetch.
*/
public int getTotalTunedQueryCount();
/**
* Return the size of the TuneQuery map.
*/
public int getTotalTunedQuerySize();
/**
* Return the size of the profile map.
*/
public int getTotalProfileSize();
}
package com.avaje.ebeaninternal.server.autofetch;
import java.util.Iterator;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.NodeUsageListener;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.AutofetchMode;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Collects and manages the the profile information.
* <p>
* The profile information is periodically converted into "tuned query details" -
* which is used to automatically tune the queries that use autoFetch.
* </p>
* <p>
* The "tuned query details" effectively are part of the query that has the
* select() and join() information (but not the where clause, order by, limits
* etc). These are applied to the query when tuneQuery() is called.
* </p>
*/
public interface AutoFetchManager extends NodeUsageListener {
/**
* Set the owning ebean server.
*/
public void setOwner(SpiEbeanServer server, ServerConfig serverConfig);
/**
* Clear the query execution statistics.
*/
public void clearQueryStatistics();
/**
* Clear all the tuned query info.
* <p>
* Should only need do this for testing and playing around.
* </p>
*/
public int clearTunedQueryInfo();
/**
* Clear all the profiling information.
* <p>
* This means the profiling information will need to be re-gathered.
* </p>
* <p>
* Should only need do this for testing and playing around.
* </p>
*/
public int clearProfilingInfo();
/**
* On shutdown fire garbage collection and collect statistics. Note that
* usually we add a little delay (100 milliseconds) to give the garbage
* collector plenty of time to do its thing and collect the profile
* information.
*/
public void shutdown();
/**
* Return the current tuned fetch information for a given queryPoint key.
*/
public TunedQueryInfo getTunedQueryInfo(String queryPointKey);
/**
* Return the current Statistics for a given queryPoint key.
*/
public Statistics getStatistics(String queryPointKey);
/**
* Iterate the tuned fetch info.
* <p>
* This should be a read only iteration.
* </p>
*/
public Iterator<TunedQueryInfo> iterateTunedQueryInfo();
/**
* Iterate the node usage statistics.
* <p>
* This should be a read only iteration.
* </p>
*/
public Iterator<Statistics> iterateStatistics();
/**
* Return true if profiling is enabled.
*/
public boolean isProfiling();
/**
* Set to true to enable profiling.
* <p>
* We rely on garbage collection to collect the profiling information. This
* means there is a unknown delay between when a query is executed and when
* we actually collect the usage profile information.
* </p>
* <p>
* Due to this garbage collection delay, when turning off profiling while
* the application is running you should consider calling
* collectUsageViaGC() <em>BEFORE</em> setProfiling(false). This hints to
* the JVM to perform garbage collection, and hopefully collects the
* profiling information.
* </p>
*/
public void setProfiling(boolean enable);
/**
* Return true if automatic query tuning is enabled.
*/
public boolean isQueryTuning();
/**
* Set to true to enable automatic query tuning.
*/
public void setQueryTuning(boolean enable);
/**
* This controls whether autoFetch is used when it has not been explicitly
* set on a query via {@link Query#setAutoFetch(boolean)}.
*/
public AutofetchMode getMode();
/**
* Set the auto fetch mode used when a query has not had
* {@link Query#setAutoFetch(boolean)}.
*/
public void setMode(AutofetchMode Mode);
/**
* Return the profiling rate (int between 0 and 100).
*/
public double getProfilingRate();
/**
* Set the profiling rate (int between 0 and 100).
*/
public void setProfilingRate(double rate);
/**
* Return the max number of queries profiled (per query point).
* <p>
* The number of queries profiled is collected per query point. Once a query
* point has profiled this number of queries it does not profile any more.
* </p>
*/
public int getProfilingBase();
/**
* Set a max number of queries to profile per query point.
* <p>
* This number should provide a level of confidence that no more profiling
* is required for this query point.
* </p>
*/
public void setProfilingBase(int profilingMax);
/**
* Return the minimum number of queries profiled before autoFetch will start
* automatically tuning the queries.
* <p>
* This could be one which means start autoFetch tuning after the first
* profiling information is collected.
* </p>
*/
public int getProfilingMin();
/**
* Set the minimum number of queries profiled per query point before
* autoFetch will automatically tune the queries.
* <p>
* Increasing this number will mean more profiling is collected before
* autoFetch starts tuning the query.
* </p>
*/
public void setProfilingMin(int autoFetchMinThreshold);
/**
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
* fires this will gather the usage profiling information.
*/
public String collectUsageViaGC(long waitMillis);
/**
* This will take the current profiling information and update the "tuned
* query detail".
* <p>
* This is done periodically and can also be manually invoked.
* </p>
* <p>
* This returns a string summary of the updates that occurred.
* </p>
*/
public String updateTunedQueryInfo();
/**
* Called when a query thinks it should be automatically tuned by autoFetch.
* <p>
* This internally checks that autoFetch is enabled, there is a "tuned query
* detail" to tune the query with and that the autoFetchMinThreshold has
* been reached.
* </p>
* <p>
* This will also determine if the query should be profiled.
* </p>
*/
public boolean tuneQuery(SpiQuery<?> query);
/**
* Collect query profiling information.
* <p>
* This is for the original query as well as any subsequent lazy loading
* queries that are required as the object graph is traversed.
* </p>
*
* @param node
* the node path in the object graph.
* @param beans
* the number of beans loaded by the query.
* @param micros
* the query executing time in microseconds
*/
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
/**
* Return the number of queries tuned by AutoFetch.
*/
public int getTotalTunedQueryCount();
/**
* Return the size of the TuneQuery map.
*/
public int getTotalTunedQuerySize();
/**
* Return the size of the profile map.
*/
public int getTotalProfileSize();
}
@@ -1,4 +1 @@
/**
* Default L2 server cache implementation.
*/
package com.avaje.ebeaninternal.server.cache;
@@ -1,83 +1,64 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
/**
* Represents a relatively small independent message.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
*
* @author rbygrave
*
*/
public class BinaryMessage {
public static final int TYPE_MSGCONTROL = 0;
public static final int TYPE_BEANIUD = 1;
public static final int TYPE_TABLEIUD = 2;
public static final int TYPE_BEANDELTA = 3;
public static final int TYPE_BEANPATHUPDATE = 4;
public static final int TYPE_INDEX_INVALIDATE = 6;
public static final int TYPE_INDEX = 7;
public static final int TYPE_MSGACK = 8;
public static final int TYPE_MSGRESEND = 9;
private final ByteArrayOutputStream buffer;
private final DataOutputStream os;
private byte[] bytes;
/**
* Create with an estimated buffer size.
*/
public BinaryMessage(int bufSize) {
this.buffer = new ByteArrayOutputStream(bufSize);
this.os = new DataOutputStream(buffer);
}
/**
* Return the DataOutputStream to write content to.
*/
public DataOutputStream getOs() {
return os;
}
/**
* Return all the content as a byte array.
*/
public byte[] getByteArray() {
if (bytes == null) {
bytes = buffer.toByteArray();
}
return bytes;
}
}
package com.avaje.ebeaninternal.server.cluster;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
/**
* Represents a relatively small independent message.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
*
* @author rbygrave
*
*/
public class BinaryMessage {
public static final int TYPE_MSGCONTROL = 0;
public static final int TYPE_BEANIUD = 1;
public static final int TYPE_TABLEIUD = 2;
public static final int TYPE_BEANDELTA = 3;
public static final int TYPE_BEANPATHUPDATE = 4;
public static final int TYPE_INDEX_INVALIDATE = 6;
public static final int TYPE_INDEX = 7;
public static final int TYPE_MSGACK = 8;
public static final int TYPE_MSGRESEND = 9;
private final ByteArrayOutputStream buffer;
private final DataOutputStream os;
private byte[] bytes;
/**
* Create with an estimated buffer size.
*/
public BinaryMessage(int bufSize) {
this.buffer = new ByteArrayOutputStream(bufSize);
this.os = new DataOutputStream(buffer);
}
/**
* Return the DataOutputStream to write content to.
*/
public DataOutputStream getOs() {
return os;
}
/**
* Return all the content as a byte array.
*/
public byte[] getByteArray() {
if (bytes == null) {
bytes = buffer.toByteArray();
}
return bytes;
}
}
@@ -1,42 +1,23 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.util.ArrayList;
import java.util.List;
/**
* Holds a List of BinaryMessage's.
*
* @author rbygrave
*/
public class BinaryMessageList {
ArrayList<BinaryMessage> list = new ArrayList<BinaryMessage>();
public void add(BinaryMessage msg) {
list.add(msg);
}
public List<BinaryMessage> 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<BinaryMessage> list = new ArrayList<BinaryMessage>();
public void add(BinaryMessage msg) {
list.add(msg);
}
public List<BinaryMessage> getList() {
return list;
}
}
@@ -1,45 +1,28 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Sends messages to the cluster members.
*/
public interface ClusterBroadcast {
/**
* Inform the other cluster members that this instance has come online and
* start any listeners etc.
*/
public void startup(ClusterManager clusterManager);
/**
* Inform the other cluster members that this instance is leaving and
* shutdown any listeners.
*/
public void shutdown();
/**
* Send a transaction event to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent);
}
package com.avaje.ebeaninternal.server.cluster;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Sends messages to the cluster members.
*/
public interface ClusterBroadcast {
/**
* Inform the other cluster members that this instance has come online and
* start any listeners etc.
*/
public void startup(ClusterManager clusterManager);
/**
* Inform the other cluster members that this instance is leaving and
* shutdown any listeners.
*/
public void shutdown();
/**
* Send a transaction event to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent);
}
@@ -1,122 +1,105 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Manages the cluster service.
*/
public class ClusterManager {
private static final Logger logger = Logger.getLogger(ClusterManager.class.getName());
private final ConcurrentHashMap<String, EbeanServer> serverMap = new ConcurrentHashMap<String, EbeanServer>();
private final Object monitor = new Object();
private final ClusterBroadcast broadcast;
private boolean started;
public ClusterManager() {
String clusterType = GlobalProperties.get("ebean.cluster.type", null);
if (clusterType == null || clusterType.trim().length() == 0) {
// not clustering this instance
this.broadcast = null;
} else {
try {
if ("mcast".equalsIgnoreCase(clusterType)) {
this.broadcast = new McastClusterManager();
} else if ("socket".equalsIgnoreCase(clusterType)) {
this.broadcast = new SocketClusterBroadcast();
} else {
logger.info("Clustering using [" + clusterType + "]");
this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
}
} catch (Exception e) {
String msg = "Error initialising ClusterManager type [" + clusterType + "]";
logger.log(Level.SEVERE, msg, e);
throw new RuntimeException(e);
}
}
}
public void registerServer(EbeanServer server) {
synchronized (monitor) {
if (!started) {
startup();
}
serverMap.put(server.getName(), server);
}
}
public EbeanServer getServer(String name) {
synchronized (monitor) {
return serverMap.get(name);
}
}
private void startup() {
started = true;
if (broadcast != null) {
broadcast.startup(this);
}
}
/**
* Return true if clustering is on.
*/
public boolean isClustering() {
return broadcast != null;
}
/**
* Send the message headers and payload to every server in the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
if (broadcast != null) {
broadcast.broadcast(remoteTransEvent);
}
}
/**
* Shutdown the service and Deregister from the cluster.
*/
public void shutdown() {
if (broadcast != null) {
logger.info("ClusterManager shutdown ");
broadcast.shutdown();
}
}
}
package com.avaje.ebeaninternal.server.cluster;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Manages the cluster service.
*/
public class ClusterManager {
private static final Logger logger = Logger.getLogger(ClusterManager.class.getName());
private final ConcurrentHashMap<String, EbeanServer> serverMap = new ConcurrentHashMap<String, EbeanServer>();
private final Object monitor = new Object();
private final ClusterBroadcast broadcast;
private boolean started;
public ClusterManager() {
String clusterType = GlobalProperties.get("ebean.cluster.type", null);
if (clusterType == null || clusterType.trim().length() == 0) {
// not clustering this instance
this.broadcast = null;
} else {
try {
if ("mcast".equalsIgnoreCase(clusterType)) {
this.broadcast = new McastClusterManager();
} else if ("socket".equalsIgnoreCase(clusterType)) {
this.broadcast = new SocketClusterBroadcast();
} else {
logger.info("Clustering using [" + clusterType + "]");
this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
}
} catch (Exception e) {
String msg = "Error initialising ClusterManager type [" + clusterType + "]";
logger.log(Level.SEVERE, msg, e);
throw new RuntimeException(e);
}
}
}
public void registerServer(EbeanServer server) {
synchronized (monitor) {
if (!started) {
startup();
}
serverMap.put(server.getName(), server);
}
}
public EbeanServer getServer(String name) {
synchronized (monitor) {
return serverMap.get(name);
}
}
private void startup() {
started = true;
if (broadcast != null) {
broadcast.startup(this);
}
}
/**
* Return true if clustering is on.
*/
public boolean isClustering() {
return broadcast != null;
}
/**
* Send the message headers and payload to every server in the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
if (broadcast != null) {
broadcast.broadcast(remoteTransEvent);
}
}
/**
* Shutdown the service and Deregister from the cluster.
*/
public void shutdown() {
if (broadcast != null) {
logger.info("ClusterManager shutdown ");
broadcast.shutdown();
}
}
}
@@ -1,43 +1,24 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.Serializable;
/**
* Simple holder of binary data.
* Used to use Packet based serialisation of RemoteTransactionEvent
* with simple Java Serialisation of the DataHolder.
*/
public class DataHolder implements Serializable {
private static final long serialVersionUID = 9090748723571322192L;
private final byte[] data;
public DataHolder(byte[] data) {
this.data = data;
}
public byte[] getData() {
return data;
}
}
package com.avaje.ebeaninternal.server.cluster;
import java.io.Serializable;
/**
* Simple holder of binary data.
* Used to use Packet based serialisation of RemoteTransactionEvent
* with simple Java Serialisation of the DataHolder.
*/
public class DataHolder implements Serializable {
private static final long serialVersionUID = 9090748723571322192L;
private final byte[] data;
public DataHolder(byte[] data) {
this.data = data;
}
public byte[] getData() {
return data;
}
}
@@ -1,212 +1,193 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Represents the contents sent as a single DatagramPacket.
* <p>
* The contents is typically multiple messages (ACK,PING etc) or all or part of
* a RemoteTransactionEvent.
* </p>
* <p>
* Due to the hard limit on the size of UDP packets a RemoteTransactionEvent
* with lots of information could be broken up into multiple packets.
* </p>
*
* @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.
* <p>
* The contents is typically multiple messages (ACK,PING etc) or all or part of
* a RemoteTransactionEvent.
* </p>
* <p>
* Due to the hard limit on the size of UDP packets a RemoteTransactionEvent
* with lots of information could be broken up into multiple packets.
* </p>
*
* @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;
}
}
@@ -1,88 +1,69 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.DataInput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebeaninternal.server.cluster.mcast.Message;
import com.avaje.ebeaninternal.server.cluster.mcast.MessageAck;
import com.avaje.ebeaninternal.server.cluster.mcast.MessageControl;
import com.avaje.ebeaninternal.server.cluster.mcast.MessageResend;
/**
* A Packet that contains Ack, Resend and Control messages.
*
* @author rbygrave
*/
public class PacketMessages extends Packet {
private final ArrayList<Message> 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<Message>();
}
/**
* Return the messages contained in this Packet.
*/
public List<Message> 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<Message> 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<Message>();
}
/**
* Return the messages contained in this Packet.
*/
public List<Message> 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);
}
}
}
@@ -1,94 +1,75 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.DataInput;
import java.io.IOException;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
import com.avaje.ebeaninternal.server.transaction.IndexEvent;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* A Packet holding TransactionEvent data.
* <p>
* Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
* is actually broken up into smaller messages.
* </p>
* @author rbygrave
*/
public class PacketTransactionEvent extends Packet {
private final SpiEbeanServer server;
private final RemoteTransactionEvent event;
public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException {
return new PacketTransactionEvent(true, packetId, timestamp, serverName);
}
private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException {
super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName);
this.server = null;
this.event = null;
}
private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException {
super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName);
this.server = server;
this.event = new RemoteTransactionEvent(server);
}
public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException {
return new PacketTransactionEvent(header, server);
}
public RemoteTransactionEvent getEvent() {
return event;
}
protected void readMessage(DataInput dataInput, int msgType) throws IOException {
switch (msgType) {
case BinaryMessage.TYPE_BEANIUD:
event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput));
break;
case BinaryMessage.TYPE_TABLEIUD:
event.addTableIUD(TableIUD.readBinaryMessage(dataInput));
break;
case BinaryMessage.TYPE_BEANDELTA:
event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
break;
case BinaryMessage.TYPE_INDEX:
event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
break;
default:
throw new RuntimeException("Invalid Transaction msgType "+msgType);
}
}
}
package com.avaje.ebeaninternal.server.cluster;
import java.io.DataInput;
import java.io.IOException;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
import com.avaje.ebeaninternal.server.transaction.IndexEvent;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* A Packet holding TransactionEvent data.
* <p>
* Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
* is actually broken up into smaller messages.
* </p>
* @author rbygrave
*/
public class PacketTransactionEvent extends Packet {
private final SpiEbeanServer server;
private final RemoteTransactionEvent event;
public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException {
return new PacketTransactionEvent(true, packetId, timestamp, serverName);
}
private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException {
super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName);
this.server = null;
this.event = null;
}
private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException {
super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName);
this.server = server;
this.event = new RemoteTransactionEvent(server);
}
public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException {
return new PacketTransactionEvent(header, server);
}
public RemoteTransactionEvent getEvent() {
return event;
}
protected void readMessage(DataInput dataInput, int msgType) throws IOException {
switch (msgType) {
case BinaryMessage.TYPE_BEANIUD:
event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput));
break;
case BinaryMessage.TYPE_TABLEIUD:
event.addTableIUD(TableIUD.readBinaryMessage(dataInput));
break;
case BinaryMessage.TYPE_BEANDELTA:
event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
break;
case BinaryMessage.TYPE_INDEX:
event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
break;
default:
throw new RuntimeException("Invalid Transaction msgType "+msgType);
}
}
}
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.IOException;
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster;
import java.io.ByteArrayInputStream;
@@ -1,62 +1,43 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.ArrayList;
import java.util.List;
/**
* Holds a list of ACK and RESEND messages that should be sent out.
*
* @author rbygrave
*/
public class AckResendMessages {
ArrayList<Message> messages = new ArrayList<Message>();
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<Message> 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<Message> messages = new ArrayList<Message>();
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<Message> getMessages() {
return messages;
}
}
@@ -1,72 +1,53 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.HashMap;
import java.util.List;
/**
* For this node this holds the ACK gotAllPoint for each member in the cluster.
* <p>
* 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.
* </p>
* Thread Safety note: Object only used by McastClusterBroadcast Manager thread.
* So Single Threaded access.
*
* @author rbygrave
*/
public class IncomingPacketsLastAck {
private HashMap<String,MessageAck> lastAckMap = new HashMap<String, MessageAck>();
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<Message> 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.
* <p>
* 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.
* </p>
* Thread Safety note: Object only used by McastClusterBroadcast Manager thread.
* So Single Threaded access.
*
* @author rbygrave
*/
public class IncomingPacketsLastAck {
private HashMap<String,MessageAck> lastAckMap = new HashMap<String, MessageAck>();
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<Message> 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);
}
}
}
}
@@ -1,292 +1,273 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* For Incoming Packets remembers the packets we have received and processed.
* <p>
* This determines the gotAllPoint per cluster member and identifies missing
* packets (gap between gotAllPoint and gotMaxPoint).
* </p>
* <p>
* 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.
* </p>
*
* @author rbygrave
*
*/
public class IncomingPacketsProcessed {
private final ConcurrentHashMap<String, GotAllPoint> mapByMember = new ConcurrentHashMap<String, GotAllPoint>();
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.
* <p>
* 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.
* </p>
*/
public static class GotAllPoint {
private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName());
private final String memberKey;
private final int maxResendIncoming;
private long gotAllPoint;
private long gotMaxPoint;
/**
* Packets received out of order.
*/
private ArrayList<Long> outOfOrderList = new ArrayList<Long>();
private HashMap<Long,Integer> resendCountMap = new HashMap<Long,Integer>();
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<Long> 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<Long> getMissingPackets() {
synchronized (this) {
ArrayList<Long> missingList = new ArrayList<Long>();
// this is not particularly efficient but expecting
// the outOfOrderList to be relatively small
boolean lostPacket = false;
for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) {
Long packetId = Long.valueOf(i);
if (!outOfOrderList.contains(packetId)) {
if (incrementResendCount(packetId)) {
// request this packet be resent
missingList.add(packetId);
} else {
lostPacket = true;
}
}
}
if (lostPacket){
checkOutOfOrderList();
}
return missingList;
}
}
/**
* Return true if this packet has not yet exceeded the maxResendCount.
*/
private boolean incrementResendCount(Long packetId){
Integer resendCount = resendCountMap.get(packetId);
if (resendCount != null){
int i = resendCount.intValue() + 1;
if (i > maxResendIncoming){
// we are going to give up trying to get this packet now
logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it.");
resendCountMap.remove(packetId);
outOfOrderList.add(packetId);
return false;
}
resendCount = Integer.valueOf(i);
resendCountMap.put(packetId, resendCount);
} else {
resendCountMap.put(packetId, ONE);
}
return true;
}
private static final Integer ONE = Integer.valueOf(1);
public boolean processPacket(long packetId) {
synchronized (this) {
if (gotAllPoint == 0) {
gotAllPoint = packetId;
return true;
}
if (packetId <= gotAllPoint) {
// already processed this packet
return false;
}
if (!resendCountMap.isEmpty()){
resendCountMap.remove(Long.valueOf(packetId));
}
if (packetId == gotAllPoint + 1) {
gotAllPoint = packetId;
} else {
if (packetId > gotMaxPoint) {
gotMaxPoint = packetId;
}
outOfOrderList.add(Long.valueOf(packetId));
}
checkOutOfOrderList();
return true;
}
}
private void checkOutOfOrderList() {
if (outOfOrderList.size() == 0) {
return;
}
boolean continueCheck;
do {
continueCheck = false;
long nextPoint = gotAllPoint + 1;
Iterator<Long> it = outOfOrderList.iterator();
while (it.hasNext()) {
Long id = it.next();
if (id.longValue() == nextPoint) {
// we found the next one in the outOfOrderList
it.remove();
gotAllPoint = nextPoint;
continueCheck = true;
break;
}
}
} while (continueCheck);
}
}
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* For Incoming Packets remembers the packets we have received and processed.
* <p>
* This determines the gotAllPoint per cluster member and identifies missing
* packets (gap between gotAllPoint and gotMaxPoint).
* </p>
* <p>
* 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.
* </p>
*
* @author rbygrave
*
*/
public class IncomingPacketsProcessed {
private final ConcurrentHashMap<String, GotAllPoint> mapByMember = new ConcurrentHashMap<String, GotAllPoint>();
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.
* <p>
* 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.
* </p>
*/
public static class GotAllPoint {
private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName());
private final String memberKey;
private final int maxResendIncoming;
private long gotAllPoint;
private long gotMaxPoint;
/**
* Packets received out of order.
*/
private ArrayList<Long> outOfOrderList = new ArrayList<Long>();
private HashMap<Long,Integer> resendCountMap = new HashMap<Long,Integer>();
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<Long> 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<Long> getMissingPackets() {
synchronized (this) {
ArrayList<Long> missingList = new ArrayList<Long>();
// this is not particularly efficient but expecting
// the outOfOrderList to be relatively small
boolean lostPacket = false;
for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) {
Long packetId = Long.valueOf(i);
if (!outOfOrderList.contains(packetId)) {
if (incrementResendCount(packetId)) {
// request this packet be resent
missingList.add(packetId);
} else {
lostPacket = true;
}
}
}
if (lostPacket){
checkOutOfOrderList();
}
return missingList;
}
}
/**
* Return true if this packet has not yet exceeded the maxResendCount.
*/
private boolean incrementResendCount(Long packetId){
Integer resendCount = resendCountMap.get(packetId);
if (resendCount != null){
int i = resendCount.intValue() + 1;
if (i > maxResendIncoming){
// we are going to give up trying to get this packet now
logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it.");
resendCountMap.remove(packetId);
outOfOrderList.add(packetId);
return false;
}
resendCount = Integer.valueOf(i);
resendCountMap.put(packetId, resendCount);
} else {
resendCountMap.put(packetId, ONE);
}
return true;
}
private static final Integer ONE = Integer.valueOf(1);
public boolean processPacket(long packetId) {
synchronized (this) {
if (gotAllPoint == 0) {
gotAllPoint = packetId;
return true;
}
if (packetId <= gotAllPoint) {
// already processed this packet
return false;
}
if (!resendCountMap.isEmpty()){
resendCountMap.remove(Long.valueOf(packetId));
}
if (packetId == gotAllPoint + 1) {
gotAllPoint = packetId;
} else {
if (packetId > gotMaxPoint) {
gotMaxPoint = packetId;
}
outOfOrderList.add(Long.valueOf(packetId));
}
checkOutOfOrderList();
return true;
}
}
private void checkOutOfOrderList() {
if (outOfOrderList.size() == 0) {
return;
}
boolean continueCheck;
do {
continueCheck = false;
long nextPoint = gotAllPoint + 1;
Iterator<Long> 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);
}
}
}
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
@@ -1,135 +1,116 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.cluster.Packet;
/**
* Handles the sending of Packets via DatagramPacket.
*
* @author rbygrave
*/
public class McastSender {
private static final Logger logger = Logger.getLogger(McastSender.class.getName());
private final int port;
private final InetAddress inetAddress;
private final DatagramSocket sock;
private final InetSocketAddress sendAddr;
private final String senderHostPort;
public McastSender(int port, String address, int sendPort, String sendAddress) {
try {
this.port = port;
this.inetAddress = InetAddress.getByName(address);
InetAddress sendInetAddress = null;
if (sendAddress != null) {
sendInetAddress = InetAddress.getByName(sendAddress);
} else {
sendInetAddress = InetAddress.getLocalHost();
}
if (sendPort > 0) {
this.sock = new DatagramSocket(sendPort, sendInetAddress);
} else {
this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0));
}
String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]";
logger.info(msg);
this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort());
this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort();
} catch (Exception e) {
String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address;
throw new RuntimeException(msg, e);
}
}
/**
* Return the send Address so that if we have loopback messages we can
* detect if they where sent by this local sender and hence should be
* ignored.
*/
public InetSocketAddress getAddress() {
return sendAddr;
}
/**
* Return the Host and Port of the sender. This is used to uniquely identify
* this instance in the cluster.
*/
public String getSenderHostPort() {
return senderHostPort;
}
/**
* Send the packet.
*/
public int sendPacket(Packet packet) throws IOException {
byte[] pktBytes = packet.getBytes();
if (logger.isLoggable(Level.FINE)){
logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length);
}
if (pktBytes.length > 65507){
logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length
+" likely to be truncated using UDP with a MAXIMUM length of 65507");
}
DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port);
sock.send(pack);
return pktBytes.length;
}
/**
* Send the list of Packets.
*/
public int sendPackets(List<Packet> packets) throws IOException {
int totalBytes = 0;
for (int i = 0; i < packets.size(); i++) {
totalBytes += sendPacket(packets.get(i));
}
return totalBytes;
}
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.cluster.Packet;
/**
* Handles the sending of Packets via DatagramPacket.
*
* @author rbygrave
*/
public class McastSender {
private static final Logger logger = Logger.getLogger(McastSender.class.getName());
private final int port;
private final InetAddress inetAddress;
private final DatagramSocket sock;
private final InetSocketAddress sendAddr;
private final String senderHostPort;
public McastSender(int port, String address, int sendPort, String sendAddress) {
try {
this.port = port;
this.inetAddress = InetAddress.getByName(address);
InetAddress sendInetAddress = null;
if (sendAddress != null) {
sendInetAddress = InetAddress.getByName(sendAddress);
} else {
sendInetAddress = InetAddress.getLocalHost();
}
if (sendPort > 0) {
this.sock = new DatagramSocket(sendPort, sendInetAddress);
} else {
this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0));
}
String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]";
logger.info(msg);
this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort());
this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort();
} catch (Exception e) {
String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address;
throw new RuntimeException(msg, e);
}
}
/**
* Return the send Address so that if we have loopback messages we can
* detect if they where sent by this local sender and hence should be
* ignored.
*/
public InetSocketAddress getAddress() {
return sendAddr;
}
/**
* Return the Host and Port of the sender. This is used to uniquely identify
* this instance in the cluster.
*/
public String getSenderHostPort() {
return senderHostPort;
}
/**
* Send the packet.
*/
public int sendPacket(Packet packet) throws IOException {
byte[] pktBytes = packet.getBytes();
if (logger.isLoggable(Level.FINE)){
logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length);
}
if (pktBytes.length > 65507){
logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length
+" likely to be truncated using UDP with a MAXIMUM length of 65507");
}
DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port);
sock.send(pack);
return pktBytes.length;
}
/**
* Send the list of Packets.
*/
public int sendPackets(List<Packet> packets) throws IOException {
int totalBytes = 0;
for (int i = 0; i < packets.size(); i++) {
totalBytes += sendPacket(packets.get(i));
}
return totalBytes;
}
}
@@ -1,155 +1,136 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
/**
* Gives an overall status of this Cluster instance.
* <p>
* Ideally you want to see relatively low Re-send statistics.
* </p>
*
* @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.
* <p>
* Ideally you want to see relatively low Re-send statistics.
* </p>
*
* @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;
}
}
@@ -1,33 +1,14 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public interface Message {
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException;
public boolean isControlMessage();
public String getToHostPort();
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public interface Message {
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException;
public boolean isControlMessage();
public String getToHostPort();
}
@@ -1,76 +1,57 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class MessageAck implements Message {
private final String toHostPort;
private final long gotAllPacketId;
public MessageAck(String toHostPort, long gotAllPacketId) {
this.toHostPort = toHostPort;
this.gotAllPacketId = gotAllPacketId;
}
public String toString() {
return "Ack "+toHostPort+" "+gotAllPacketId;
}
public boolean isControlMessage() {
return false;
}
public String getToHostPort() {
return toHostPort;
}
public long getGotAllPacketId() {
return gotAllPacketId;
}
public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException {
String hostPort = dataInput.readUTF();
long gotAllPacketId = dataInput.readLong();
return new MessageAck(hostPort, gotAllPacketId);
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
DataOutputStream os = m.getOs();
os.writeInt(BinaryMessage.TYPE_MSGACK);
os.writeUTF(toHostPort);
os.writeLong(gotAllPacketId);
os.flush();
msgList.add(m);
}
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class MessageAck implements Message {
private final String toHostPort;
private final long gotAllPacketId;
public MessageAck(String toHostPort, long gotAllPacketId) {
this.toHostPort = toHostPort;
this.gotAllPacketId = gotAllPacketId;
}
public String toString() {
return "Ack "+toHostPort+" "+gotAllPacketId;
}
public boolean isControlMessage() {
return false;
}
public String getToHostPort() {
return toHostPort;
}
public long getGotAllPacketId() {
return gotAllPacketId;
}
public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException {
String hostPort = dataInput.readUTF();
long gotAllPacketId = dataInput.readLong();
return new MessageAck(hostPort, gotAllPacketId);
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20);
DataOutputStream os = m.getOs();
os.writeInt(BinaryMessage.TYPE_MSGACK);
os.writeUTF(toHostPort);
os.writeLong(gotAllPacketId);
os.flush();
msgList.add(m);
}
}
@@ -1,92 +1,73 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class MessageControl implements Message {
public static final short TYPE_JOIN = 1;
public static final short TYPE_LEAVE = 2;
public static final short TYPE_PING = 3;
public static final short TYPE_JOINRESPONSE = 7;
public static final short TYPE_PINGRESPONSE = 8;
private final short controlType;
private final String fromHostPort;
public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException {
short controlType = dataInput.readShort();
String hostPort = dataInput.readUTF();
return new MessageControl(controlType, hostPort);
}
public MessageControl(short controlType, String helloFromHostPort) {
this.controlType = controlType;
this.fromHostPort = helloFromHostPort;
}
public String toString() {
switch (controlType) {
case TYPE_JOIN: return "Join "+fromHostPort;
case TYPE_LEAVE: return "Leave "+fromHostPort;
case TYPE_PING: return "Ping "+fromHostPort;
case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort;
default:
throw new RuntimeException("Invalid controlType "+controlType);
}
}
public boolean isControlMessage() {
return true;
}
public short getControlType() {
return controlType;
}
public String getToHostPort() {
return "*";
}
public String getFromHostPort() {
return fromHostPort;
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10);
DataOutputStream os = m.getOs();
os.writeInt(BinaryMessage.TYPE_MSGCONTROL);
os.writeShort(controlType);
os.writeUTF(fromHostPort);
os.flush();
msgList.add(m);
}
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class MessageControl implements Message {
public static final short TYPE_JOIN = 1;
public static final short TYPE_LEAVE = 2;
public static final short TYPE_PING = 3;
public static final short TYPE_JOINRESPONSE = 7;
public static final short TYPE_PINGRESPONSE = 8;
private final short controlType;
private final String fromHostPort;
public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException {
short controlType = dataInput.readShort();
String hostPort = dataInput.readUTF();
return new MessageControl(controlType, hostPort);
}
public MessageControl(short controlType, String helloFromHostPort) {
this.controlType = controlType;
this.fromHostPort = helloFromHostPort;
}
public String toString() {
switch (controlType) {
case TYPE_JOIN: return "Join "+fromHostPort;
case TYPE_LEAVE: return "Leave "+fromHostPort;
case TYPE_PING: return "Ping "+fromHostPort;
case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort;
default:
throw new RuntimeException("Invalid controlType "+controlType);
}
}
public boolean isControlMessage() {
return true;
}
public short getControlType() {
return controlType;
}
public String getToHostPort() {
return "*";
}
public String getFromHostPort() {
return fromHostPort;
}
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10);
DataOutputStream os = m.getOs();
os.writeInt(BinaryMessage.TYPE_MSGCONTROL);
os.writeShort(controlType);
os.writeUTF(fromHostPort);
os.flush();
msgList.add(m);
}
}
@@ -1,96 +1,77 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
public class MessageResend implements Message {
private final String toHostPort;
private final List<Long> resendPacketIds;
public MessageResend(String toHostPort, List<Long> resendPacketIds) {
this.toHostPort = toHostPort;
this.resendPacketIds = resendPacketIds;
}
public MessageResend(String toHostPort) {
this(toHostPort, new ArrayList<Long>(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<Long> 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<Long> resendPacketIds;
public MessageResend(String toHostPort, List<Long> resendPacketIds) {
this.toHostPort = toHostPort;
this.resendPacketIds = resendPacketIds;
}
public MessageResend(String toHostPort) {
this(toHostPort, new ArrayList<Long>(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<Long> 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);
}
}
@@ -1,125 +1,106 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.HashMap;
import java.util.Map;
public class OutgoingPacketsAcked {
private long minimumGotAllPacketId;
private Map<String, GroupMemberAck> recievedByMap = new HashMap<String, GroupMemberAck>();
public int getGroupSize() {
synchronized (this) {
return recievedByMap.size();
}
}
public long getMinimumGotAllPacketId() {
synchronized (this) {
return minimumGotAllPacketId;
}
}
public void removeMember(String groupMember){
synchronized (this) {
recievedByMap.remove(groupMember);
resetGotAllMin();
}
}
private boolean resetGotAllMin() {
long tempMin;
if (recievedByMap.isEmpty()){
//System.out.println(" -- -- -- -- "+recievedByMap.isEmpty());
tempMin = Long.MAX_VALUE;
} else {
tempMin = Long.MAX_VALUE;
}
for (GroupMemberAck groupMemAck : recievedByMap.values()) {
long memberMin = groupMemAck.getGotAllPacketId();
if (memberMin < tempMin){
//System.out.println(" -- new tmpMin "+memberMin);
tempMin = memberMin;
}
}
if (tempMin != minimumGotAllPacketId) {
minimumGotAllPacketId = tempMin;
return true;
} else {
return false;
}
}
public long receivedAck(String groupMember, MessageAck ack) {
synchronized (this) {
boolean checkMin = false;
GroupMemberAck groupMemberAck = recievedByMap.get(groupMember);
if (groupMemberAck == null) {
//System.out.println(" -- new groupMemberAck");
groupMemberAck = new GroupMemberAck();
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
recievedByMap.put(groupMember, groupMemberAck);
checkMin = true;
} else {
checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId;
//System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId());
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
}
boolean minChanged = false;
//System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId);
if (checkMin || minimumGotAllPacketId == 0){
minChanged = resetGotAllMin();
//System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId);
}
return minChanged ? minimumGotAllPacketId : 0;
}
}
private static class GroupMemberAck {
private long gotAllPacketId;
private GroupMemberAck() {
}
private long getGotAllPacketId() {
return gotAllPacketId;
}
private void setIfBigger(long newGotAll) {
if (newGotAll > gotAllPacketId) {
gotAllPacketId = newGotAll;
}
}
}
}
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.HashMap;
import java.util.Map;
public class OutgoingPacketsAcked {
private long minimumGotAllPacketId;
private Map<String, GroupMemberAck> recievedByMap = new HashMap<String, GroupMemberAck>();
public int getGroupSize() {
synchronized (this) {
return recievedByMap.size();
}
}
public long getMinimumGotAllPacketId() {
synchronized (this) {
return minimumGotAllPacketId;
}
}
public void removeMember(String groupMember){
synchronized (this) {
recievedByMap.remove(groupMember);
resetGotAllMin();
}
}
private boolean resetGotAllMin() {
long tempMin;
if (recievedByMap.isEmpty()){
//System.out.println(" -- -- -- -- "+recievedByMap.isEmpty());
tempMin = Long.MAX_VALUE;
} else {
tempMin = Long.MAX_VALUE;
}
for (GroupMemberAck groupMemAck : recievedByMap.values()) {
long memberMin = groupMemAck.getGotAllPacketId();
if (memberMin < tempMin){
//System.out.println(" -- new tmpMin "+memberMin);
tempMin = memberMin;
}
}
if (tempMin != minimumGotAllPacketId) {
minimumGotAllPacketId = tempMin;
return true;
} else {
return false;
}
}
public long receivedAck(String groupMember, MessageAck ack) {
synchronized (this) {
boolean checkMin = false;
GroupMemberAck groupMemberAck = recievedByMap.get(groupMember);
if (groupMemberAck == null) {
//System.out.println(" -- new groupMemberAck");
groupMemberAck = new GroupMemberAck();
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
recievedByMap.put(groupMember, groupMemberAck);
checkMin = true;
} else {
checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId;
//System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId());
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
}
boolean minChanged = false;
//System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId);
if (checkMin || minimumGotAllPacketId == 0){
minChanged = resetGotAllMin();
//System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId);
}
return minChanged ? minimumGotAllPacketId : 0;
}
}
private static class GroupMemberAck {
private long gotAllPacketId;
private GroupMemberAck() {
}
private long getGotAllPacketId() {
return gotAllPacketId;
}
private void setIfBigger(long newGotAll) {
if (newGotAll > gotAllPacketId) {
gotAllPacketId = newGotAll;
}
}
}
}
@@ -1,85 +1,66 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.mcast;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.avaje.ebeaninternal.server.cluster.Packet;
/**
* Cache of the outgoing packets.
* <p>
* These are held until we receive ACKs from the other members of the cluster to
* say they have received the packets.
* </p>
*
* @author rbygrave
*
*/
public class OutgoingPacketsCache {
private final Map<Long, Packet> packetMap = new TreeMap<Long, Packet>();
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<Packet> 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<Long> 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.
* <p>
* These are held until we receive ACKs from the other members of the cluster to
* say they have received the packets.
* </p>
*
* @author rbygrave
*
*/
public class OutgoingPacketsCache {
private final Map<Long, Packet> packetMap = new TreeMap<Long, Packet>();
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<Packet> 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<Long> it = packetMap.keySet().iterator();
while (it.hasNext()) {
Long pktId = it.next();
if (minAcked >= pktId.longValue()) {
it.remove();
}
}
}
}
@@ -1,76 +1,59 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This parses and dispatches a request to the appropriate handler.
* <p>
* Looks up the appropriate RequestHandler
* and then gets it to process the Client request.<P>
* </p>
* Note that this is a Runnable because it is assigned to the ThreadPool.
*/
class RequestProcessor implements Runnable {
private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName());
private final Socket clientSocket;
private final SocketClusterBroadcast owner;
/**
* Create including the Listener (used to lookup the Request Handler) and
* the socket itself.
*/
public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
this.clientSocket = clientSocket;
this.owner = owner;
}
/**
* This will parse out the command. Lookup the appropriate Handler and
* pass the information to the handler for processing.
* <P>Dev Note: the command parsing is processed here so that it is preformed
* by the assigned thread rather than the listeners thread.</P>
*/
public void run() {
try {
SocketConnection sc = new SocketConnection(clientSocket);
while(true){
if (owner.process(sc)) {
// got the offline message or timeout
break;
}
}
sc.disconnect();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, null, e);
}
}
};
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This parses and dispatches a request to the appropriate handler.
* <p>
* Looks up the appropriate RequestHandler
* and then gets it to process the Client request.<P>
* </p>
* Note that this is a Runnable because it is assigned to the ThreadPool.
*/
class RequestProcessor implements Runnable {
private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName());
private final Socket clientSocket;
private final SocketClusterBroadcast owner;
/**
* Create including the Listener (used to lookup the Request Handler) and
* the socket itself.
*/
public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
this.clientSocket = clientSocket;
this.owner = owner;
}
/**
* This will parse out the command. Lookup the appropriate Handler and
* pass the information to the handler for processing.
* <P>Dev Note: the command parsing is processed here so that it is preformed
* by the assigned thread rather than the listeners thread.</P>
*/
public void run() {
try {
SocketConnection sc = new SocketConnection(clientSocket);
while(true){
if (owner.process(sc)) {
// got the offline message or timeout
break;
}
}
sc.disconnect();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, null, e);
}
}
};
@@ -1,151 +1,134 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The client side of the socket clustering.
*/
class SocketClient {
private static final Logger logger = Logger.getLogger(SocketClient.class.getName());
private final InetSocketAddress address;
private final String hostPort;
private boolean online;
private Socket socket;
private OutputStream os;
private ObjectOutputStream oos;
/**
* Construct with an IP address and port.
*/
public SocketClient(InetSocketAddress address) {
this.address = address;
this.hostPort = address.getHostName()+":"+address.getPort();
}
public String getHostPort() {
return hostPort;
}
public int getPort() {
return address.getPort();
}
public boolean isOnline() {
return online;
}
public void setOnline(boolean online) throws IOException {
if (online){
setOnline();
} else {
disconnect();
}
}
/**
* Set whether the client is thought to be online.
*/
private void setOnline() throws IOException {
connect();
this.online = true;
}
public void reconnect() throws IOException {
disconnect();
connect();
}
private void connect() throws IOException {
if (socket != null){
throw new IllegalStateException("Already got a socket connection?");
}
Socket s = new Socket();
s.setKeepAlive(true);
s.connect(address);
this.socket = s;
this.os = socket.getOutputStream();
}
public void disconnect() {
this.online = false;
if (socket != null){
try {
socket.close();
} catch (IOException e) {
String msg = "Error disconnecting from Cluster member "+hostPort;
logger.log(Level.INFO, msg, e);
}
os = null;
oos = null;
socket = null;
}
}
public boolean register(SocketClusterMessage registerMsg) {
try {
setOnline();
send(registerMsg);
return true;
} catch (IOException e) {
disconnect();
return false;
}
}
public boolean send(SocketClusterMessage msg) throws IOException {
if (online){
writeObject(msg);
return true;
} else {
return false;
}
}
private void writeObject(Object object) throws IOException {
if (oos == null){
this.oos = new ObjectOutputStream(os);
}
oos.writeObject(object);
oos.flush();
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The client side of the socket clustering.
*/
class SocketClient {
private static final Logger logger = Logger.getLogger(SocketClient.class.getName());
private final InetSocketAddress address;
private final String hostPort;
private boolean online;
private Socket socket;
private OutputStream os;
private ObjectOutputStream oos;
/**
* Construct with an IP address and port.
*/
public SocketClient(InetSocketAddress address) {
this.address = address;
this.hostPort = address.getHostName()+":"+address.getPort();
}
public String getHostPort() {
return hostPort;
}
public int getPort() {
return address.getPort();
}
public boolean isOnline() {
return online;
}
public void setOnline(boolean online) throws IOException {
if (online){
setOnline();
} else {
disconnect();
}
}
/**
* Set whether the client is thought to be online.
*/
private void setOnline() throws IOException {
connect();
this.online = true;
}
public void reconnect() throws IOException {
disconnect();
connect();
}
private void connect() throws IOException {
if (socket != null){
throw new IllegalStateException("Already got a socket connection?");
}
Socket s = new Socket();
s.setKeepAlive(true);
s.connect(address);
this.socket = s;
this.os = socket.getOutputStream();
}
public void disconnect() {
this.online = false;
if (socket != null){
try {
socket.close();
} catch (IOException e) {
String msg = "Error disconnecting from Cluster member "+hostPort;
logger.log(Level.INFO, msg, e);
}
os = null;
oos = null;
socket = null;
}
}
public boolean register(SocketClusterMessage registerMsg) {
try {
setOnline();
send(registerMsg);
return true;
} catch (IOException e) {
disconnect();
return false;
}
}
public boolean send(SocketClusterMessage msg) throws IOException {
if (online){
writeObject(msg);
return true;
} else {
return false;
}
}
private void writeObject(Object object) throws IOException {
if (oos == null){
this.oos = new ObjectOutputStream(os);
}
oos.writeObject(object);
oos.flush();
}
}
@@ -1,265 +1,248 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.cluster.DataHolder;
import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Broadcast messages across the cluster using sockets.
*/
public class SocketClusterBroadcast implements ClusterBroadcast {
private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName());
private final SocketClient local;
private final HashMap<String,SocketClient> clientMap;
private final SocketClusterListener listener;
private SocketClient[] members;
private ClusterManager clusterManager;
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
private final AtomicInteger txnOutgoing = new AtomicInteger();
private final AtomicInteger txnIncoming = new AtomicInteger();
public SocketClusterBroadcast( ){
String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
String members = GlobalProperties.get("ebean.cluster.members", null);
logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
this.local = new SocketClient(parseFullName(localHostPort));
this.clientMap = new HashMap<String, SocketClient>();
String[] memArray = StringHelper.delimitedToArray(members, ",", false);
for (int i = 0; i < memArray.length; i++) {
InetSocketAddress member = parseFullName(memArray[i]);
SocketClient client = new SocketClient(member);
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
// don't add the local one ...
clientMap.put(client.getHostPort(), client);
}
}
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
this.listener = new SocketClusterListener(this, local.getPort());
}
/**
* Return the current status of this instance.
*/
public SocketClusterStatus getStatus() {
// count of online members
int currentGroupSize = 0;
for (int i = 0; i < members.length; i++) {
if (members[i].isOnline()) {
++currentGroupSize;
}
}
int txnIn = txnIncoming.get();
int txnOut = txnOutgoing.get();
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
}
public void startup(ClusterManager clusterManager) {
this.clusterManager = clusterManager;
try {
listener.startListening();
register();
} catch (IOException e) {
throw new PersistenceException(e);
}
}
public void shutdown() {
deregister();
listener.shutdown();
}
/**
* Register with all the other members of the Cluster.
*/
private void register() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
for (int i = 0; i < members.length; i++) {
boolean online = members[i].register(h);
String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
logger.info(msg);
}
}
protected void setMemberOnline(String fullName, boolean online) throws IOException {
synchronized (clientMap) {
String msg = "Cluster Member ["+fullName+"] online["+online+"]";
logger.info(msg);
SocketClient member = clientMap.get(fullName);
member.setOnline(online);
}
}
private void send(SocketClient client, SocketClusterMessage msg) {
try {
// alternative would be to connect/disconnect here
// but prefer to use keepalive
client.send(msg);
} catch (Exception ex){
logger.log(Level.SEVERE, "Error sending message", ex);
try {
client.reconnect();
} catch (IOException e) {
logger.log(Level.SEVERE, "Error trying to reconnect", ex);
}
}
}
/**
* Send the payload to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
try {
txnOutgoing.incrementAndGet();
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
broadcast(msg);
} catch (Exception e){
String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
logger.log(Level.SEVERE, msg, e);
}
}
protected void broadcast(SocketClusterMessage msg) {
for (int i = 0; i < members.length; i++) {
send(members[i], msg);
}
}
/**
* Leave the cluster.
*/
private void deregister() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
broadcast(h);
for (int i = 0; i < members.length; i++) {
members[i].disconnect();
}
}
/**
* Process a Cluster message.
*/
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
try {
SocketClusterMessage h = (SocketClusterMessage)request.readObject();
if (h.isRegisterEvent()){
setMemberOnline(h.getRegisterHost(), h.isRegister());
} else {
txnIncoming.incrementAndGet();
DataHolder dataHolder = h.getDataHolder();
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
transEvent.run();
}
if (h.isRegisterEvent() && !h.isRegister()){
// instance shutting down
return true;
} else {
return false;
}
} catch (InterruptedIOException e) {
String msg = "Timeout waiting for message";
logger.log(Level.INFO, msg, e);
try {
request.disconnect();
} catch (IOException ex){
logger.log(Level.INFO, "Error disconnecting after timeout", ex);
}
return true;
}
}
/**
* Parse a host:port into a InetSocketAddress.
*/
private InetSocketAddress parseFullName(String hostAndPort) {
try {
hostAndPort = hostAndPort.trim();
int colonPos = hostAndPort.indexOf(":");
if (colonPos == -1) {
String msg = "No colon \":\" in "+hostAndPort;
throw new IllegalArgumentException(msg);
}
String host = hostAndPort.substring(0, colonPos);
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
int port = Integer.parseInt(sPort);
return new InetSocketAddress(host, port);
} catch (Exception ex){
throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
}
}
class TxnSerialiseHelper extends SerialiseTransactionHelper {
@Override
public SpiEbeanServer getEbeanServer(String serverName) {
return (SpiEbeanServer)clusterManager.getServer(serverName);
}
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.cluster.DataHolder;
import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
/**
* Broadcast messages across the cluster using sockets.
*/
public class SocketClusterBroadcast implements ClusterBroadcast {
private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName());
private final SocketClient local;
private final HashMap<String,SocketClient> clientMap;
private final SocketClusterListener listener;
private SocketClient[] members;
private ClusterManager clusterManager;
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
private final AtomicInteger txnOutgoing = new AtomicInteger();
private final AtomicInteger txnIncoming = new AtomicInteger();
public SocketClusterBroadcast( ){
String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
String members = GlobalProperties.get("ebean.cluster.members", null);
logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
this.local = new SocketClient(parseFullName(localHostPort));
this.clientMap = new HashMap<String, SocketClient>();
String[] memArray = StringHelper.delimitedToArray(members, ",", false);
for (int i = 0; i < memArray.length; i++) {
InetSocketAddress member = parseFullName(memArray[i]);
SocketClient client = new SocketClient(member);
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
// don't add the local one ...
clientMap.put(client.getHostPort(), client);
}
}
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
this.listener = new SocketClusterListener(this, local.getPort());
}
/**
* Return the current status of this instance.
*/
public SocketClusterStatus getStatus() {
// count of online members
int currentGroupSize = 0;
for (int i = 0; i < members.length; i++) {
if (members[i].isOnline()) {
++currentGroupSize;
}
}
int txnIn = txnIncoming.get();
int txnOut = txnOutgoing.get();
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
}
public void startup(ClusterManager clusterManager) {
this.clusterManager = clusterManager;
try {
listener.startListening();
register();
} catch (IOException e) {
throw new PersistenceException(e);
}
}
public void shutdown() {
deregister();
listener.shutdown();
}
/**
* Register with all the other members of the Cluster.
*/
private void register() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
for (int i = 0; i < members.length; i++) {
boolean online = members[i].register(h);
String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
logger.info(msg);
}
}
protected void setMemberOnline(String fullName, boolean online) throws IOException {
synchronized (clientMap) {
String msg = "Cluster Member ["+fullName+"] online["+online+"]";
logger.info(msg);
SocketClient member = clientMap.get(fullName);
member.setOnline(online);
}
}
private void send(SocketClient client, SocketClusterMessage msg) {
try {
// alternative would be to connect/disconnect here
// but prefer to use keepalive
client.send(msg);
} catch (Exception ex){
logger.log(Level.SEVERE, "Error sending message", ex);
try {
client.reconnect();
} catch (IOException e) {
logger.log(Level.SEVERE, "Error trying to reconnect", ex);
}
}
}
/**
* Send the payload to all the members of the cluster.
*/
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
try {
txnOutgoing.incrementAndGet();
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
broadcast(msg);
} catch (Exception e){
String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
logger.log(Level.SEVERE, msg, e);
}
}
protected void broadcast(SocketClusterMessage msg) {
for (int i = 0; i < members.length; i++) {
send(members[i], msg);
}
}
/**
* Leave the cluster.
*/
private void deregister() {
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
broadcast(h);
for (int i = 0; i < members.length; i++) {
members[i].disconnect();
}
}
/**
* Process a Cluster message.
*/
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
try {
SocketClusterMessage h = (SocketClusterMessage)request.readObject();
if (h.isRegisterEvent()){
setMemberOnline(h.getRegisterHost(), h.isRegister());
} else {
txnIncoming.incrementAndGet();
DataHolder dataHolder = h.getDataHolder();
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
transEvent.run();
}
if (h.isRegisterEvent() && !h.isRegister()){
// instance shutting down
return true;
} else {
return false;
}
} catch (InterruptedIOException e) {
String msg = "Timeout waiting for message";
logger.log(Level.INFO, msg, e);
try {
request.disconnect();
} catch (IOException ex){
logger.log(Level.INFO, "Error disconnecting after timeout", ex);
}
return true;
}
}
/**
* Parse a host:port into a InetSocketAddress.
*/
private InetSocketAddress parseFullName(String hostAndPort) {
try {
hostAndPort = hostAndPort.trim();
int colonPos = hostAndPort.indexOf(":");
if (colonPos == -1) {
String msg = "No colon \":\" in "+hostAndPort;
throw new IllegalArgumentException(msg);
}
String host = hostAndPort.substring(0, colonPos);
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
int port = Integer.parseInt(sPort);
return new InetSocketAddress(host, port);
} catch (Exception ex){
throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
}
}
class TxnSerialiseHelper extends SerialiseTransactionHelper {
@Override
public SpiEbeanServer getEbeanServer(String serverName) {
return (SpiEbeanServer)clusterManager.getServer(serverName);
}
}
}
@@ -1,181 +1,164 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager;
/**
* Serverside multithreaded socket listener. Accepts connections and dispatches
* them to an appropriate handler.
* <p>
* 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).
* </p>
* <p>
* It has its own daemon background thread that handles the accept() loop on the
* ServerSocket.
* </p>
*/
class SocketClusterListener implements Runnable {
private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName());
/**
* The port the SocketListener uses.
*/
private final int port;
/**
* The length of the socket accept timeout.
*/
private final int listenTimeout = 60000;
/**
* The server socket used to listen for requests.
*/
private final ServerSocket serverListenSocket;
/**
* The listening thread.
*/
private final Thread listenerThread;
/**
* The pool of threads that actually do the parsing execution of requests.
*/
private final ThreadPool threadPool;
private final SocketClusterBroadcast owner;
/**
* shutting down flag.
*/
boolean doingShutdown;
/**
* Whether the listening thread is busy assigning a request to a thread.
*/
boolean isActive;
/**
* Construct with a given thread pool name.
*/
public SocketClusterListener(SocketClusterBroadcast owner, int port) {
this.owner = owner;
this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember");
this.port = port;
try {
this.serverListenSocket = new ServerSocket(port);
this.serverListenSocket.setSoTimeout(listenTimeout);
this.listenerThread = new Thread(this, "EbeanClusterListener");
} catch (IOException e){
String msg = "Error starting cluster socket listener on port "+port;
throw new RuntimeException(msg,e);
}
}
/**
* Returns the port the listener is using.
*/
public int getPort() {
return port;
}
/**
* Start listening for requests.
*/
public void startListening() throws IOException {
this.listenerThread.setDaemon(true);
this.listenerThread.start();
}
/**
* Shutdown this listener.
*/
public void shutdown() {
doingShutdown = true;
try {
if (isActive) {
synchronized (listenerThread) {
try {
listenerThread.wait(1000);
} catch (InterruptedException e) {
// OK to ignore as expected to Interrupt for shutdown.
;
}
}
}
listenerThread.interrupt();
serverListenSocket.close();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
}
/**
* This is a runnable and so this must be public. Don't call this externally
* but rather call the startListening() method.
*/
public void run() {
// run in loop until doingShutdown is true...
while (!doingShutdown) {
try {
synchronized (listenerThread) {
Socket clientSocket = serverListenSocket.accept();
isActive = true;
Runnable request = new RequestProcessor(owner, clientSocket);
threadPool.assign(request, true);
isActive = false;
}
} catch (SocketException e) {
if (doingShutdown) {
String msg = "doingShutdown and accept threw:"+ e.getMessage();
logger.info(msg);
} else {
logger.log(Level.SEVERE, null, e);
}
} catch (InterruptedIOException e) {
// this will happen when the server is very quiet.
// that is, no requests
logger.fine("Possibly expected due to accept timeout?" + e.getMessage());
} catch (IOException e) {
// log it and continue in the loop...
logger.log(Level.SEVERE, null, e);
}
}
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager;
/**
* Serverside multithreaded socket listener. Accepts connections and dispatches
* them to an appropriate handler.
* <p>
* 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).
* </p>
* <p>
* It has its own daemon background thread that handles the accept() loop on the
* ServerSocket.
* </p>
*/
class SocketClusterListener implements Runnable {
private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName());
/**
* The port the SocketListener uses.
*/
private final int port;
/**
* The length of the socket accept timeout.
*/
private final int listenTimeout = 60000;
/**
* The server socket used to listen for requests.
*/
private final ServerSocket serverListenSocket;
/**
* The listening thread.
*/
private final Thread listenerThread;
/**
* The pool of threads that actually do the parsing execution of requests.
*/
private final ThreadPool threadPool;
private final SocketClusterBroadcast owner;
/**
* shutting down flag.
*/
boolean doingShutdown;
/**
* Whether the listening thread is busy assigning a request to a thread.
*/
boolean isActive;
/**
* Construct with a given thread pool name.
*/
public SocketClusterListener(SocketClusterBroadcast owner, int port) {
this.owner = owner;
this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember");
this.port = port;
try {
this.serverListenSocket = new ServerSocket(port);
this.serverListenSocket.setSoTimeout(listenTimeout);
this.listenerThread = new Thread(this, "EbeanClusterListener");
} catch (IOException e){
String msg = "Error starting cluster socket listener on port "+port;
throw new RuntimeException(msg,e);
}
}
/**
* Returns the port the listener is using.
*/
public int getPort() {
return port;
}
/**
* Start listening for requests.
*/
public void startListening() throws IOException {
this.listenerThread.setDaemon(true);
this.listenerThread.start();
}
/**
* Shutdown this listener.
*/
public void shutdown() {
doingShutdown = true;
try {
if (isActive) {
synchronized (listenerThread) {
try {
listenerThread.wait(1000);
} catch (InterruptedException e) {
// OK to ignore as expected to Interrupt for shutdown.
;
}
}
}
listenerThread.interrupt();
serverListenSocket.close();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
}
/**
* This is a runnable and so this must be public. Don't call this externally
* but rather call the startListening() method.
*/
public void run() {
// run in loop until doingShutdown is true...
while (!doingShutdown) {
try {
synchronized (listenerThread) {
Socket clientSocket = serverListenSocket.accept();
isActive = true;
Runnable request = new RequestProcessor(owner, clientSocket);
threadPool.assign(request, true);
isActive = false;
}
} catch (SocketException e) {
if (doingShutdown) {
String msg = "doingShutdown and accept threw:"+ e.getMessage();
logger.info(msg);
} else {
logger.log(Level.SEVERE, null, e);
}
} catch (InterruptedIOException e) {
// this will happen when the server is very quiet.
// that is, no requests
logger.fine("Possibly expected due to accept timeout?" + e.getMessage());
} catch (IOException e) {
// log it and continue in the loop...
logger.log(Level.SEVERE, null, e);
}
}
}
}
@@ -1,95 +1,78 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.Serializable;
import com.avaje.ebeaninternal.server.cluster.DataHolder;
import com.avaje.ebeaninternal.server.cluster.Packet;
/**
* The messages broadcast around the cluster.
*/
public class SocketClusterMessage implements Serializable {
private static final long serialVersionUID = 2993350408394934473L;
private final String registerHost;
private final boolean register;
private final DataHolder dataHolder;
public static SocketClusterMessage register(String registerHost, boolean register){
return new SocketClusterMessage(registerHost, register);
}
public static SocketClusterMessage transEvent(DataHolder transEvent){
return new SocketClusterMessage(transEvent);
}
public static SocketClusterMessage packet(Packet packet){
DataHolder d = new DataHolder(packet.getBytes());
return new SocketClusterMessage(d);
}
/**
* Used to construct a Child AttributeMap.
*/
private SocketClusterMessage(String registerHost, boolean register) {
this.registerHost = registerHost;
this.register = register;
this.dataHolder = null;
}
private SocketClusterMessage(DataHolder dataHolder) {
this.dataHolder = dataHolder;
this.registerHost = null;
this.register = false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (registerHost != null){
sb.append("register ");
sb.append(register);
sb.append(" ");
sb.append(registerHost);
} else {
sb.append("transEvent ");
}
return sb.toString();
}
public boolean isRegisterEvent() {
return registerHost != null;
}
public String getRegisterHost() {
return registerHost;
}
public boolean isRegister() {
return register;
}
public DataHolder getDataHolder() {
return dataHolder;
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.Serializable;
import com.avaje.ebeaninternal.server.cluster.DataHolder;
import com.avaje.ebeaninternal.server.cluster.Packet;
/**
* The messages broadcast around the cluster.
*/
public class SocketClusterMessage implements Serializable {
private static final long serialVersionUID = 2993350408394934473L;
private final String registerHost;
private final boolean register;
private final DataHolder dataHolder;
public static SocketClusterMessage register(String registerHost, boolean register){
return new SocketClusterMessage(registerHost, register);
}
public static SocketClusterMessage transEvent(DataHolder transEvent){
return new SocketClusterMessage(transEvent);
}
public static SocketClusterMessage packet(Packet packet){
DataHolder d = new DataHolder(packet.getBytes());
return new SocketClusterMessage(d);
}
/**
* Used to construct a Child AttributeMap.
*/
private SocketClusterMessage(String registerHost, boolean register) {
this.registerHost = registerHost;
this.register = register;
this.dataHolder = null;
}
private SocketClusterMessage(DataHolder dataHolder) {
this.dataHolder = dataHolder;
this.registerHost = null;
this.register = false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (registerHost != null){
sb.append("register ");
sb.append(register);
sb.append(" ");
sb.append(registerHost);
} else {
sb.append("transEvent ");
}
return sb.toString();
}
public boolean isRegisterEvent() {
return registerHost != null;
}
public String getRegisterHost() {
return registerHost;
}
public boolean isRegister() {
return register;
}
public DataHolder getDataHolder() {
return dataHolder;
}
}
@@ -1,60 +1,41 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
/**
* The current state of this cluster member.
*
* @author rbygrave
*/
public class SocketClusterStatus {
private final int currentGroupSize;
private final int txnIncoming;
private final int txtOutgoing;
public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) {
this.currentGroupSize = currentGroupSize;
this.txnIncoming = txnIncoming;
this.txtOutgoing = txnOutgoing;
}
/**
* Return the number of members of the cluster currently online.
*/
public int getCurrentGroupSize() {
return currentGroupSize;
}
/**
* Return the number of Remote transactions received.
*/
public int getTxnIncoming() {
return txnIncoming;
}
/**
* Return the number of transactions sent to the cluster.
*/
public int getTxtOutgoing() {
return txtOutgoing;
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
/**
* The current state of this cluster member.
*
* @author rbygrave
*/
public class SocketClusterStatus {
private final int currentGroupSize;
private final int txnIncoming;
private final int txtOutgoing;
public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) {
this.currentGroupSize = currentGroupSize;
this.txnIncoming = txnIncoming;
this.txtOutgoing = txnOutgoing;
}
/**
* Return the number of members of the cluster currently online.
*/
public int getCurrentGroupSize() {
return currentGroupSize;
}
/**
* Return the number of Remote transactions received.
*/
public int getTxnIncoming() {
return txnIncoming;
}
/**
* Return the number of transactions sent to the cluster.
*/
public int getTxtOutgoing() {
return txtOutgoing;
}
}
@@ -1,146 +1,129 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* The client side of a TCP Sockect connection.
*/
class SocketConnection {
/**
* The object underlying objectOutputStream.
*/
ObjectOutputStream oos;
/**
* The underlying ObjectInputStream.
*/
ObjectInputStream ois;
/**
* The underlying inputStream.
*/
InputStream is;
/**
* The underlying outputStream.
*/
OutputStream os;
/**
* The underlying socket.
*/
Socket socket;
/**
* Create for a given Socket.
*/
public SocketConnection(Socket socket) throws IOException {
this.is = socket.getInputStream();
this.os = socket.getOutputStream();
this.socket = socket;
}
/**
* Disconnect from the server.
*/
public void disconnect() throws IOException {
os.flush();
socket.close();
}
/**
* Flush the outputStream.
*/
public void flush() throws IOException {
os.flush();
}
/**
* Read an object from the object input stream.
*/
public Object readObject() throws IOException, ClassNotFoundException {
return getObjectInputStream().readObject();
}
/**
* Write an object to the object output stream.
*/
public ObjectOutputStream writeObject(Object object) throws IOException {
ObjectOutputStream oos = getObjectOutputStream();
oos.writeObject(object);
return oos;
}
/**
* Get the object output stream.
*/
public ObjectOutputStream getObjectOutputStream() throws IOException {
if (oos == null){
oos = new ObjectOutputStream(os);
}
return oos;
}
/**
* Get the object input stream.
*/
public ObjectInputStream getObjectInputStream() throws IOException {
if (ois == null){
ois = new ObjectInputStream(is);
}
return ois;
}
/**
* Set the ObjectInputStream to use.
*/
public void setObjectInputStream(ObjectInputStream ois) {
this.ois = ois;
}
/**
* Set the ObjectOutputStream to use.
*/
public void setObjectOutputStream(ObjectOutputStream oos) {
this.oos = oos;
}
/**
* Return the underlying input stream.
*/
public InputStream getInputStream() throws IOException {
return is;
}
/**
* Return the underlying output stream.
*/
public OutputStream getOutputStream() throws IOException {
return os;
}
}
package com.avaje.ebeaninternal.server.cluster.socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* The client side of a TCP Sockect connection.
*/
class SocketConnection {
/**
* The object underlying objectOutputStream.
*/
ObjectOutputStream oos;
/**
* The underlying ObjectInputStream.
*/
ObjectInputStream ois;
/**
* The underlying inputStream.
*/
InputStream is;
/**
* The underlying outputStream.
*/
OutputStream os;
/**
* The underlying socket.
*/
Socket socket;
/**
* Create for a given Socket.
*/
public SocketConnection(Socket socket) throws IOException {
this.is = socket.getInputStream();
this.os = socket.getOutputStream();
this.socket = socket;
}
/**
* Disconnect from the server.
*/
public void disconnect() throws IOException {
os.flush();
socket.close();
}
/**
* Flush the outputStream.
*/
public void flush() throws IOException {
os.flush();
}
/**
* Read an object from the object input stream.
*/
public Object readObject() throws IOException, ClassNotFoundException {
return getObjectInputStream().readObject();
}
/**
* Write an object to the object output stream.
*/
public ObjectOutputStream writeObject(Object object) throws IOException {
ObjectOutputStream oos = getObjectOutputStream();
oos.writeObject(object);
return oos;
}
/**
* Get the object output stream.
*/
public ObjectOutputStream getObjectOutputStream() throws IOException {
if (oos == null){
oos = new ObjectOutputStream(os);
}
return oos;
}
/**
* Get the object input stream.
*/
public ObjectInputStream getObjectInputStream() throws IOException {
if (ois == null){
ois = new ObjectInputStream(is);
}
return ois;
}
/**
* Set the ObjectInputStream to use.
*/
public void setObjectInputStream(ObjectInputStream ois) {
this.ois = ois;
}
/**
* Set the ObjectOutputStream to use.
*/
public void setObjectOutputStream(ObjectOutputStream oos) {
this.oos = oos;
}
/**
* Return the underlying input stream.
*/
public InputStream getInputStream() throws IOException {
return is;
}
/**
* Return the underlying output stream.
*/
public OutputStream getOutputStream() throws IOException {
return os;
}
}
@@ -1,493 +1,474 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
/**
* Default implementation of TypeConverter.
* <p>
* Converts objects to the required type if required.
* </p>
*/
public final class BasicTypeConverter implements Serializable {
private static final long serialVersionUID = 7691463236204070311L;
/**
* Type code for java.util.Calendar.
*/
public static final int UTIL_CALENDAR = -999998986;
/**
* Type code for java.util.Date.
*/
public static final int UTIL_DATE = -999998988;
/**
* Type code for java.math.BigInteger.
*/
public static final int MATH_BIGINTEGER = -999998987;
/**
* Type code for an Enum type.
*/
public static final int ENUM = -999998989;
private BasicTypeConverter() {
}
/**
* Convert the Object to the required data type.
*
* @param value
* the Object value
* @param toDataType
* the dataType as per java.sql.Types.
*/
public static Object convert(Object value, int toDataType) {
try {
switch (toDataType) {
case UTIL_DATE: {
return toUtilDate(value);
}
case UTIL_CALENDAR: {
return toCalendar(value);
}
case Types.BIGINT: {
return toLong(value);
}
case Types.INTEGER: {
return toInteger(value);
}
case Types.BIT: {
return toBoolean(value);
}
case Types.TINYINT: {
return toByte(value);
}
case Types.SMALLINT: {
return toShort(value);
}
case Types.NUMERIC: {
return toBigDecimal(value);
}
case Types.DECIMAL: {
return toBigDecimal(value);
}
case Types.REAL: {
return toFloat(value);
}
case Types.DOUBLE: {
return toDouble(value);
}
case Types.FLOAT: {
return toDouble(value);
}
case Types.BOOLEAN: {
return toBoolean(value);
}
case Types.TIMESTAMP: {
return toTimestamp(value);
}
case Types.DATE: {
return toDate(value);
}
case Types.VARCHAR: {
return toString(value);
}
case Types.CHAR: {
return toString(value);
}
case Types.OTHER: {
return value;
}
case Types.JAVA_OBJECT: {
return value;
}
case Types.BINARY:
case Types.LONGVARBINARY:
case Types.BLOB: {
return value;
}
case Types.LONGVARCHAR:
case Types.CLOB: {
return value;
}
default: {
String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
throw new RuntimeException(msg);
}
}
} catch (ClassCastException e) {
String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
throw new RuntimeException(m);
}
}
/**
* Convert the value to a String.
*/
public static String toString(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof char[]) {
return String.valueOf((char[]) value);
}
return value.toString();
}
/**
* Convert the value to a Boolean with an explicit String true value.
*/
public static Boolean toBoolean(Object value, String dbTrueValue) {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
String s = value.toString();
return s.equalsIgnoreCase(dbTrueValue);
}
/**
* Convert the value to a Boolean. Can be a Boolean or the string values
* "true" or "false".
*/
public static Boolean toBoolean(Object value) {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
return Boolean.valueOf(value.toString());
}
/**
* Convert the value to a UUID.
*/
public static UUID toUUID(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return UUID.fromString((String) value);
}
return (UUID) value;
}
/**
* convert the passed in object to a BigDecimal. It should be another
* numeric type.
*/
public static BigDecimal toBigDecimal(Object value) {
if (value == null) {
return null;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(value.toString());
}
public static Float toFloat(Object value) {
if (value == null) {
return null;
}
if (value instanceof Float) {
return (Float) value;
}
if (value instanceof Number) {
return Float.valueOf(((Number) value).floatValue());
}
return Float.valueOf(value.toString());
}
public static Short toShort(Object value) {
if (value == null) {
return null;
}
if (value instanceof Short) {
return (Short) value;
}
if (value instanceof Number) {
return Short.valueOf(((Number) value).shortValue());
}
return Short.valueOf(value.toString());
}
public static Byte toByte(Object value) {
if (value == null) {
return null;
}
if (value instanceof Byte) {
return (Byte) value;
}
return Byte.valueOf(value.toString());
}
/**
* convert the passed in object to a Integer. It should be another numeric
* type.
*/
public static Integer toInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Number) {
return Integer.valueOf(((Number) value).intValue());
}
return Integer.valueOf(value.toString());
}
/**
* Convert the object to a Long. It should be another numeric type.
*/
public static Long toLong(Object value) {
if (value == null) {
return null;
}
if (value instanceof Long) {
return (Long) value;
}
if (value instanceof String) {
return Long.valueOf((String) value);
}
if (value instanceof Number) {
return Long.valueOf(((Number) value).longValue());
}
if (value instanceof java.util.Date) {
return Long.valueOf(((java.util.Date) value).getTime());
}
if (value instanceof Calendar) {
return Long.valueOf(((Calendar) value).getTime().getTime());
}
return Long.valueOf(value.toString());
}
public static BigInteger toMathBigInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof BigInteger) {
return (BigInteger) value;
}
return new BigInteger(value.toString());
}
/**
* Convert the object to a Double. It should be another numberic type.
*/
public static Double toDouble(Object value) {
if (value == null) {
return null;
}
if (value instanceof Double) {
return (Double) value;
}
if (value instanceof Number) {
return Double.valueOf(((Number) value).doubleValue());
}
return Double.valueOf(value.toString());
}
/**
* convert the passed in object to a Timestamp. It is expected to be a
* java.sql.Date really.
*/
public static Timestamp toTimestamp(Object value) {
if (value == null) {
return null;
}
if (value instanceof Timestamp) {
return (Timestamp) value;
} else if (value instanceof java.util.Date) {
// no nanos here... so hopefully ok
return new Timestamp(((java.util.Date) value).getTime());
} else if (value instanceof Calendar) {
return new Timestamp(((Calendar) value).getTime().getTime());
} else if (value instanceof String) {
return Timestamp.valueOf((String) value);
} else if (value instanceof Number) {
return new Timestamp(((Number) value).longValue());
} else {
String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
throw new RuntimeException(msg);
}
}
public static java.sql.Time toTime(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Time) {
return (java.sql.Time) value;
} else if (value instanceof String) {
return java.sql.Time.valueOf((String) value);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static java.sql.Date toDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Date) {
return (java.sql.Date) value;
} else if (value instanceof java.util.Date) {
return new java.sql.Date(((java.util.Date) value).getTime());
} else if (value instanceof Calendar) {
return new java.sql.Date(((Calendar) value).getTime().getTime());
} else if (value instanceof String) {
return java.sql.Date.valueOf((String) value);
} else if (value instanceof Number) {
return new java.sql.Date(((Number) value).longValue());
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static java.util.Date toUtilDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Timestamp) {
// loss of nanos precision
return new java.util.Date(((java.sql.Timestamp) value).getTime());
}
// DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
// java.util.Date? equals() is symmetrical so perhaps this is not
// really required?
if (value instanceof java.sql.Date) {
return new java.util.Date(((java.sql.Date) value).getTime());
}
if (value instanceof java.util.Date) {
return (java.util.Date) value;
} else if (value instanceof Calendar) {
return ((Calendar) value).getTime();
} else if (value instanceof String) {
return new java.util.Date(Timestamp.valueOf((String) value).getTime());
} else if (value instanceof Number) {
return new java.util.Date(((Number) value).longValue());
} else {
throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static Calendar toCalendar(Object value) {
if (value == null) {
return null;
}
if (value instanceof Calendar) {
return (Calendar) value;
} else if (value instanceof java.util.Date) {
java.util.Date date = ((java.util.Date) value);
return toCalendarFromDate(date);
} else if (value instanceof String) {
java.util.Date date = toUtilDate(value);
return toCalendarFromDate(date);
} else if (value instanceof Number) {
long timeMillis = ((Number) value).longValue();
java.util.Date date = new java.util.Date(timeMillis);
return toCalendarFromDate(date);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
throw new RuntimeException(m);
}
}
private static Calendar toCalendarFromDate(java.util.Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
}
package com.avaje.ebeaninternal.server.core;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
/**
* Default implementation of TypeConverter.
* <p>
* Converts objects to the required type if required.
* </p>
*/
public final class BasicTypeConverter implements Serializable {
private static final long serialVersionUID = 7691463236204070311L;
/**
* Type code for java.util.Calendar.
*/
public static final int UTIL_CALENDAR = -999998986;
/**
* Type code for java.util.Date.
*/
public static final int UTIL_DATE = -999998988;
/**
* Type code for java.math.BigInteger.
*/
public static final int MATH_BIGINTEGER = -999998987;
/**
* Type code for an Enum type.
*/
public static final int ENUM = -999998989;
private BasicTypeConverter() {
}
/**
* Convert the Object to the required data type.
*
* @param value
* the Object value
* @param toDataType
* the dataType as per java.sql.Types.
*/
public static Object convert(Object value, int toDataType) {
try {
switch (toDataType) {
case UTIL_DATE: {
return toUtilDate(value);
}
case UTIL_CALENDAR: {
return toCalendar(value);
}
case Types.BIGINT: {
return toLong(value);
}
case Types.INTEGER: {
return toInteger(value);
}
case Types.BIT: {
return toBoolean(value);
}
case Types.TINYINT: {
return toByte(value);
}
case Types.SMALLINT: {
return toShort(value);
}
case Types.NUMERIC: {
return toBigDecimal(value);
}
case Types.DECIMAL: {
return toBigDecimal(value);
}
case Types.REAL: {
return toFloat(value);
}
case Types.DOUBLE: {
return toDouble(value);
}
case Types.FLOAT: {
return toDouble(value);
}
case Types.BOOLEAN: {
return toBoolean(value);
}
case Types.TIMESTAMP: {
return toTimestamp(value);
}
case Types.DATE: {
return toDate(value);
}
case Types.VARCHAR: {
return toString(value);
}
case Types.CHAR: {
return toString(value);
}
case Types.OTHER: {
return value;
}
case Types.JAVA_OBJECT: {
return value;
}
case Types.BINARY:
case Types.LONGVARBINARY:
case Types.BLOB: {
return value;
}
case Types.LONGVARCHAR:
case Types.CLOB: {
return value;
}
default: {
String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
throw new RuntimeException(msg);
}
}
} catch (ClassCastException e) {
String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
throw new RuntimeException(m);
}
}
/**
* Convert the value to a String.
*/
public static String toString(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof char[]) {
return String.valueOf((char[]) value);
}
return value.toString();
}
/**
* Convert the value to a Boolean with an explicit String true value.
*/
public static Boolean toBoolean(Object value, String dbTrueValue) {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
String s = value.toString();
return s.equalsIgnoreCase(dbTrueValue);
}
/**
* Convert the value to a Boolean. Can be a Boolean or the string values
* "true" or "false".
*/
public static Boolean toBoolean(Object value) {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
return Boolean.valueOf(value.toString());
}
/**
* Convert the value to a UUID.
*/
public static UUID toUUID(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return UUID.fromString((String) value);
}
return (UUID) value;
}
/**
* convert the passed in object to a BigDecimal. It should be another
* numeric type.
*/
public static BigDecimal toBigDecimal(Object value) {
if (value == null) {
return null;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(value.toString());
}
public static Float toFloat(Object value) {
if (value == null) {
return null;
}
if (value instanceof Float) {
return (Float) value;
}
if (value instanceof Number) {
return Float.valueOf(((Number) value).floatValue());
}
return Float.valueOf(value.toString());
}
public static Short toShort(Object value) {
if (value == null) {
return null;
}
if (value instanceof Short) {
return (Short) value;
}
if (value instanceof Number) {
return Short.valueOf(((Number) value).shortValue());
}
return Short.valueOf(value.toString());
}
public static Byte toByte(Object value) {
if (value == null) {
return null;
}
if (value instanceof Byte) {
return (Byte) value;
}
return Byte.valueOf(value.toString());
}
/**
* convert the passed in object to a Integer. It should be another numeric
* type.
*/
public static Integer toInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Number) {
return Integer.valueOf(((Number) value).intValue());
}
return Integer.valueOf(value.toString());
}
/**
* Convert the object to a Long. It should be another numeric type.
*/
public static Long toLong(Object value) {
if (value == null) {
return null;
}
if (value instanceof Long) {
return (Long) value;
}
if (value instanceof String) {
return Long.valueOf((String) value);
}
if (value instanceof Number) {
return Long.valueOf(((Number) value).longValue());
}
if (value instanceof java.util.Date) {
return Long.valueOf(((java.util.Date) value).getTime());
}
if (value instanceof Calendar) {
return Long.valueOf(((Calendar) value).getTime().getTime());
}
return Long.valueOf(value.toString());
}
public static BigInteger toMathBigInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof BigInteger) {
return (BigInteger) value;
}
return new BigInteger(value.toString());
}
/**
* Convert the object to a Double. It should be another numberic type.
*/
public static Double toDouble(Object value) {
if (value == null) {
return null;
}
if (value instanceof Double) {
return (Double) value;
}
if (value instanceof Number) {
return Double.valueOf(((Number) value).doubleValue());
}
return Double.valueOf(value.toString());
}
/**
* convert the passed in object to a Timestamp. It is expected to be a
* java.sql.Date really.
*/
public static Timestamp toTimestamp(Object value) {
if (value == null) {
return null;
}
if (value instanceof Timestamp) {
return (Timestamp) value;
} else if (value instanceof java.util.Date) {
// no nanos here... so hopefully ok
return new Timestamp(((java.util.Date) value).getTime());
} else if (value instanceof Calendar) {
return new Timestamp(((Calendar) value).getTime().getTime());
} else if (value instanceof String) {
return Timestamp.valueOf((String) value);
} else if (value instanceof Number) {
return new Timestamp(((Number) value).longValue());
} else {
String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
throw new RuntimeException(msg);
}
}
public static java.sql.Time toTime(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Time) {
return (java.sql.Time) value;
} else if (value instanceof String) {
return java.sql.Time.valueOf((String) value);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static java.sql.Date toDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Date) {
return (java.sql.Date) value;
} else if (value instanceof java.util.Date) {
return new java.sql.Date(((java.util.Date) value).getTime());
} else if (value instanceof Calendar) {
return new java.sql.Date(((Calendar) value).getTime().getTime());
} else if (value instanceof String) {
return java.sql.Date.valueOf((String) value);
} else if (value instanceof Number) {
return new java.sql.Date(((Number) value).longValue());
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static java.util.Date toUtilDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Timestamp) {
// loss of nanos precision
return new java.util.Date(((java.sql.Timestamp) value).getTime());
}
// DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
// java.util.Date? equals() is symmetrical so perhaps this is not
// really required?
if (value instanceof java.sql.Date) {
return new java.util.Date(((java.sql.Date) value).getTime());
}
if (value instanceof java.util.Date) {
return (java.util.Date) value;
} else if (value instanceof Calendar) {
return ((Calendar) value).getTime();
} else if (value instanceof String) {
return new java.util.Date(Timestamp.valueOf((String) value).getTime());
} else if (value instanceof Number) {
return new java.util.Date(((Number) value).longValue());
} else {
throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static Calendar toCalendar(Object value) {
if (value == null) {
return null;
}
if (value instanceof Calendar) {
return (Calendar) value;
} else if (value instanceof java.util.Date) {
java.util.Date date = ((java.util.Date) value);
return toCalendarFromDate(date);
} else if (value instanceof String) {
java.util.Date date = toUtilDate(value);
return toCalendarFromDate(date);
} else if (value instanceof Number) {
long timeMillis = ((Number) value).longValue();
java.util.Date date = new java.util.Date(timeMillis);
return toCalendarFromDate(date);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
throw new RuntimeException(m);
}
}
private static Calendar toCalendarFromDate(java.util.Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
}
@@ -1,146 +1,127 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.LogLevel;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
/**
* Base class for find and persist requests.
*/
public abstract class BeanRequest {
/**
* The server processing the request.
*/
final SpiEbeanServer ebeanServer;
final String serverName;
/**
* The transaction this is part of.
*/
SpiTransaction transaction;
boolean createdTransaction;
boolean readOnly;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.serverName = ebeanServer.getName();
this.transaction = t;
}
/**
* initialise an implicit transaction if one is not currently supplied.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
*/
public abstract void initTransIfRequired();
/**
* A helper method for creating an implicit transaction is it is required.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
*/
public void createImplicitTransIfRequired(boolean readOnlyTransaction) {
if (transaction == null) {
transaction = ebeanServer.getCurrentServerTransaction();
if (transaction == null || !transaction.isActive()) {
// create an implicit transaction to execute this query
transaction = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
//if (readOnlyTransaction) {
// readOnly = true;
// transaction.setReadOnly(true);
//}
createdTransaction = true;
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
if (readOnly) {
transaction.rollback();
} else {
transaction.commit();
}
}
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
transaction.rollback();
}
}
/**
* Return the server processing the request. Made available for
* BeanController and BeanFinder.
*/
public EbeanServer getEbeanServer() {
return ebeanServer;
}
/**
* Return the Transaction associated with this request.
*/
public SpiTransaction getTransaction() {
return transaction;
}
/**
* Returns the connection from the Transaction.
*/
public Connection getConnection() {
return transaction.getInternalConnection();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
}
}
package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.LogLevel;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
/**
* Base class for find and persist requests.
*/
public abstract class BeanRequest {
/**
* The server processing the request.
*/
final SpiEbeanServer ebeanServer;
final String serverName;
/**
* The transaction this is part of.
*/
SpiTransaction transaction;
boolean createdTransaction;
boolean readOnly;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.serverName = ebeanServer.getName();
this.transaction = t;
}
/**
* initialise an implicit transaction if one is not currently supplied.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
*/
public abstract void initTransIfRequired();
/**
* A helper method for creating an implicit transaction is it is required.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
*/
public void createImplicitTransIfRequired(boolean readOnlyTransaction) {
if (transaction == null) {
transaction = ebeanServer.getCurrentServerTransaction();
if (transaction == null || !transaction.isActive()) {
// create an implicit transaction to execute this query
transaction = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
//if (readOnlyTransaction) {
// readOnly = true;
// transaction.setReadOnly(true);
//}
createdTransaction = true;
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
if (readOnly) {
transaction.rollback();
} else {
transaction.commit();
}
}
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
transaction.rollback();
}
}
/**
* Return the server processing the request. Made available for
* BeanController and BeanFinder.
*/
public EbeanServer getEbeanServer() {
return ebeanServer;
}
/**
* Return the Transaction associated with this request.
*/
public SpiTransaction getTransaction() {
return transaction;
}
/**
* Returns the connection from the Transaction.
*/
public Connection getConnection() {
return transaction.getInternalConnection();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
}
}
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.List;
@@ -1,460 +1,441 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.avaje.ebean.annotation.LdapDomain;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassPathSearchMatcher {
private static final Logger logger = Logger.getLogger(BootupClasses.class.getName());
private ArrayList<Class<?>> xmlBeanList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> embeddableList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> entityList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> scalarTypeList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> scalarConverterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> compoundTypeList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanControllerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> transactionEventListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFinderList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> luceneIndexList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private List<BeanPersistController> persistControllerInstances = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListenerInstances = new ArrayList<BeanPersistListener<?>>();
private List<BeanQueryAdapter> queryAdapterInstances = new ArrayList<BeanQueryAdapter>();
private List<TransactionEventListener> transactionEventListenerInstances = new ArrayList<TransactionEventListener>();
public BootupClasses() {
}
public BootupClasses(List<Class<?>> list) {
if (list != null) {
process(list.iterator());
}
}
private BootupClasses(BootupClasses parent) {
this.xmlBeanList.addAll(parent.xmlBeanList);
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.luceneIndexList.addAll(parent.luceneIndexList);
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
private void process(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> cls = it.next();
isMatch(cls);
}
}
/**
* 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.log(Level.SEVERE, msg, e);
}
}
}
public void addQueryAdapters(List<BeanQueryAdapter> 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<BeanPersistController> 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<TransactionEventListener> 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<BeanPersistListener<?>> 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<ServerConfigStartup> startupInstances) {
if (startupInstances != null) {
for (ServerConfigStartup l : startupInstances) {
this.serverConfigStartupInstances.add(l);
// don't automatically instantiate
this.serverConfigStartupList.remove(l.getClass());
}
}
}
public List<BeanQueryAdapter> 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.log(Level.SEVERE, msg, e);
}
}
return queryAdapterInstances;
}
public List<BeanPersistListener<?>> 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.log(Level.SEVERE, msg, e);
}
}
return persistListenerInstances;
}
public List<BeanPersistController> 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.log(Level.SEVERE, msg, e);
}
}
return persistControllerInstances;
}
public List<TransactionEventListener> 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.log(Level.SEVERE, msg, e);
}
}
return transactionEventListenerInstances;
}
/**
* Return the list of Embeddable classes.
*/
public ArrayList<Class<?>> getEmbeddables() {
return embeddableList;
}
/**
* Return the list of entity classes.
*/
public ArrayList<Class<?>> getEntities() {
return entityList;
}
/**
* Return the list of ScalarTypes found.
*/
public ArrayList<Class<?>> getScalarTypes() {
return scalarTypeList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getScalarConverters() {
return scalarConverterList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getCompoundTypes() {
return compoundTypeList;
}
/**
* Return the list of BeanControllers found.
*/
public ArrayList<Class<?>> getBeanControllers() {
return beanControllerList;
}
/**
* Return the list of TransactionEventListeners found
*/
public ArrayList<Class<?>> getTransactionEventListenerList() {
return transactionEventListenerList;
}
/**
* Return the list of BeanFinders found.
*/
public ArrayList<Class<?>> getBeanFinders() {
return beanFinderList;
}
/**
* Return the list of BeanListeners found.
*/
public ArrayList<Class<?>> getBeanListeners() {
return beanListenerList;
}
/**
* Return the list of XML Beans.
*/
public ArrayList<Class<?>> getXmlBeanList() {
return xmlBeanList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
isMatch(clazz);
}
}
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
} else if (isEntity(cls)) {
entityList.add(cls);
} else if (isXmlBean(cls)){
entityList.add(cls);
//xmlBeanList.add(cls);
} else if (isInterestingInterface(cls)) {
return true;
} else {
return false;
}
return true;
}
/**
* Look for interesting interfaces.
* <p>
* This includes ScalarType, BeanController, BeanFinder and BeanListener.
* </p>
*/
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;
}
ann = cls.getAnnotation(LdapDomain.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;
}
private boolean isXmlBean(Class<?> cls) {
Annotation ann = cls.getAnnotation(XmlRootElement.class);
if (ann != null) {
return true;
}
ann = cls.getAnnotation(XmlType.class);
if (ann != null) {
// Only looking for Beans and not Enums
return !cls.isEnum();
}
return false;
}
}
package com.avaje.ebeaninternal.server.core;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.avaje.ebean.annotation.LdapDomain;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassPathSearchMatcher {
private static final Logger logger = Logger.getLogger(BootupClasses.class.getName());
private ArrayList<Class<?>> xmlBeanList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> embeddableList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> entityList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> scalarTypeList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> scalarConverterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> compoundTypeList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanControllerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> transactionEventListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFinderList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> luceneIndexList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private List<BeanPersistController> persistControllerInstances = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListenerInstances = new ArrayList<BeanPersistListener<?>>();
private List<BeanQueryAdapter> queryAdapterInstances = new ArrayList<BeanQueryAdapter>();
private List<TransactionEventListener> transactionEventListenerInstances = new ArrayList<TransactionEventListener>();
public BootupClasses() {
}
public BootupClasses(List<Class<?>> list) {
if (list != null) {
process(list.iterator());
}
}
private BootupClasses(BootupClasses parent) {
this.xmlBeanList.addAll(parent.xmlBeanList);
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.luceneIndexList.addAll(parent.luceneIndexList);
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
private void process(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> cls = it.next();
isMatch(cls);
}
}
/**
* 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.log(Level.SEVERE, msg, e);
}
}
}
public void addQueryAdapters(List<BeanQueryAdapter> 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<BeanPersistController> 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<TransactionEventListener> 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<BeanPersistListener<?>> 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<ServerConfigStartup> startupInstances) {
if (startupInstances != null) {
for (ServerConfigStartup l : startupInstances) {
this.serverConfigStartupInstances.add(l);
// don't automatically instantiate
this.serverConfigStartupList.remove(l.getClass());
}
}
}
public List<BeanQueryAdapter> 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.log(Level.SEVERE, msg, e);
}
}
return queryAdapterInstances;
}
public List<BeanPersistListener<?>> 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.log(Level.SEVERE, msg, e);
}
}
return persistListenerInstances;
}
public List<BeanPersistController> 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.log(Level.SEVERE, msg, e);
}
}
return persistControllerInstances;
}
public List<TransactionEventListener> 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.log(Level.SEVERE, msg, e);
}
}
return transactionEventListenerInstances;
}
/**
* Return the list of Embeddable classes.
*/
public ArrayList<Class<?>> getEmbeddables() {
return embeddableList;
}
/**
* Return the list of entity classes.
*/
public ArrayList<Class<?>> getEntities() {
return entityList;
}
/**
* Return the list of ScalarTypes found.
*/
public ArrayList<Class<?>> getScalarTypes() {
return scalarTypeList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getScalarConverters() {
return scalarConverterList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getCompoundTypes() {
return compoundTypeList;
}
/**
* Return the list of BeanControllers found.
*/
public ArrayList<Class<?>> getBeanControllers() {
return beanControllerList;
}
/**
* Return the list of TransactionEventListeners found
*/
public ArrayList<Class<?>> getTransactionEventListenerList() {
return transactionEventListenerList;
}
/**
* Return the list of BeanFinders found.
*/
public ArrayList<Class<?>> getBeanFinders() {
return beanFinderList;
}
/**
* Return the list of BeanListeners found.
*/
public ArrayList<Class<?>> getBeanListeners() {
return beanListenerList;
}
/**
* Return the list of XML Beans.
*/
public ArrayList<Class<?>> getXmlBeanList() {
return xmlBeanList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
isMatch(clazz);
}
}
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
} else if (isEntity(cls)) {
entityList.add(cls);
} else if (isXmlBean(cls)){
entityList.add(cls);
//xmlBeanList.add(cls);
} else if (isInterestingInterface(cls)) {
return true;
} else {
return false;
}
return true;
}
/**
* Look for interesting interfaces.
* <p>
* This includes ScalarType, BeanController, BeanFinder and BeanListener.
* </p>
*/
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;
}
ann = cls.getAnnotation(LdapDomain.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;
}
private boolean isXmlBean(Class<?> cls) {
Annotation ann = cls.getAnnotation(XmlRootElement.class);
if (ann != null) {
return true;
}
ann = cls.getAnnotation(XmlType.class);
if (ann != null) {
// Only looking for Beans and not Enums
return !cls.isEnum();
}
return false;
}
}
@@ -1,191 +1,172 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.H2Platform;
import com.avaje.ebean.config.dbplatform.HsqldbPlatform;
import com.avaje.ebean.config.dbplatform.MsSqlServer2000Platform;
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
import com.avaje.ebean.config.dbplatform.Oracle10Platform;
import com.avaje.ebean.config.dbplatform.Oracle9Platform;
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
import com.avaje.ebean.config.dbplatform.SqlAnywherePlatform;
/**
* Create a DatabasePlatform from the configuration.
* <p>
* Will used platform name or use the meta data from the JDBC driver to
* determine the platform automatically.
* </p>
*/
public class DatabasePlatformFactory {
private static final Logger logger = Logger.getLogger(DatabasePlatformFactory.class.getName());
/**
* 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("postgres83")) {
return new PostgresPlatform();
}
if (dbName.equals("oracle9")) {
return new Oracle9Platform();
}
if (dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("oracle")) {
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("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.log(Level.SEVERE, 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.indexOf("oracle") > -1) {
if (majorVersion > 9) {
return new Oracle10Platform();
} else {
return new Oracle9Platform();
}
}
if (dbProductName.indexOf("microsoft") > -1) {
if (majorVersion > 8) {
return new MsSqlServer2005Platform();
} else {
return new MsSqlServer2000Platform();
}
}
if (dbProductName.indexOf("mysql") > -1) {
return new MySqlPlatform();
}
if (dbProductName.indexOf("h2") > -1) {
return new H2Platform();
}
if (dbProductName.indexOf("hsql database engine") > -1) {
return new HsqldbPlatform();
}
if (dbProductName.indexOf("postgres") > -1) {
return new PostgresPlatform();
}
if (dbProductName.indexOf("sqlite") > -1) {
return new SQLitePlatform();
}
if (dbProductName.indexOf("sql anywhere") > -1) {
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 java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.H2Platform;
import com.avaje.ebean.config.dbplatform.HsqldbPlatform;
import com.avaje.ebean.config.dbplatform.MsSqlServer2000Platform;
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
import com.avaje.ebean.config.dbplatform.Oracle10Platform;
import com.avaje.ebean.config.dbplatform.Oracle9Platform;
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
import com.avaje.ebean.config.dbplatform.SqlAnywherePlatform;
/**
* Create a DatabasePlatform from the configuration.
* <p>
* Will used platform name or use the meta data from the JDBC driver to
* determine the platform automatically.
* </p>
*/
public class DatabasePlatformFactory {
private static final Logger logger = Logger.getLogger(DatabasePlatformFactory.class.getName());
/**
* 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("postgres83")) {
return new PostgresPlatform();
}
if (dbName.equals("oracle9")) {
return new Oracle9Platform();
}
if (dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("oracle")) {
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("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.log(Level.SEVERE, 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.indexOf("oracle") > -1) {
if (majorVersion > 9) {
return new Oracle10Platform();
} else {
return new Oracle9Platform();
}
}
if (dbProductName.indexOf("microsoft") > -1) {
if (majorVersion > 8) {
return new MsSqlServer2005Platform();
} else {
return new MsSqlServer2000Platform();
}
}
if (dbProductName.indexOf("mysql") > -1) {
return new MySqlPlatform();
}
if (dbProductName.indexOf("h2") > -1) {
return new H2Platform();
}
if (dbProductName.indexOf("hsql database engine") > -1) {
return new HsqldbPlatform();
}
if (dbProductName.indexOf("postgres") > -1) {
return new PostgresPlatform();
}
if (dbProductName.indexOf("sqlite") > -1) {
return new SQLitePlatform();
}
if (dbProductName.indexOf("sql anywhere") > -1) {
return new SqlAnywherePlatform();
}
// use the standard one
return new DatabasePlatform();
}
}
@@ -1,71 +1,52 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.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.
*
* @author rbygrave
*/
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final DaemonThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param mainPoolSize
* the core size of the thread pool.
* @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 mainPoolSize, int schedulePoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonThreadPool(mainPoolSize, 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.
*
* @author rbygrave
*/
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final DaemonThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param mainPoolSize
* the core size of the thread pool.
* @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 mainPoolSize, int schedulePoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonThreadPool(mainPoolSize, 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();
}
}
@@ -1,478 +1,459 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityNotFoundException;
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.LoadBeanContext;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
import com.avaje.ebeaninternal.api.LoadManyContext;
import com.avaje.ebeaninternal.api.LoadManyRequest;
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.transaction.DefaultPersistenceContext;
/**
* Helper to handle lazy loading and refreshing of beans.
*
* @author rbygrave
*/
public class DefaultBeanLoader {
private static final Logger logger = Logger.getLogger(DefaultBeanLoader.class.getName());
private final DebugLazyLoad debugLazyLoad;
private final DefaultServer server;
protected DefaultBeanLoader(DefaultServer server, DebugLazyLoad debugLazyLoad) {
this.server = server;
this.debugLazyLoad = debugLazyLoad;
}
/**
* Return a batch size that might be less than the requestedBatchSize.
* <p>
* This means we can have large and variable requestedBatchSizes.
* </p>
* <p>
* 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.
* </p>
*/
private int getBatchSize(int batchListSize, int requestedBatchSize) {
if (batchListSize == requestedBatchSize) {
return batchListSize;
}
if (batchListSize == 1) {
// there is only one bean/collection to load
return 1;
}
if (requestedBatchSize <= 5) {
// anything less than 5 becomes 5
return 5;
}
if (batchListSize <= 10 || requestedBatchSize <= 10) {
// 10 or less to load
// ... or we wanted a batch size between 6 and 10
return 10;
}
if (batchListSize <= 20 || requestedBatchSize <= 20) {
// 20 or less to load
// ... or we wanted a batch size between 11 and 20
return 20;
}
if (batchListSize <= 50) {
return 50;
}
return requestedBatchSize;
}
public void refreshMany(Object parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void loadMany(LoadManyRequest loadRequest) {
List<BeanCollection<?>> batch = loadRequest.getBatch();
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
LoadManyContext ctx = loadRequest.getLoadContext();
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
PersistenceContext pc = ctx.getPersistenceContext();
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
for (int i = 0; i < batch.size(); i++) {
BeanCollection<?> bc = batch.get(i);
Object 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();
String idProperty = desc.getIdBinder().getIdProperty();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
query.setMode(Mode.LAZYLOAD_MANY);
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
query.select(idProperty);
query.fetch(many.getName());
if (idList.size() == 1) {
query.where().idEq(idList.get(0));
} else {
query.where().idIn(idList);
}
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
// potentially changes the joins and selected properties
ctx.configureQuery(query);
if (loadRequest.isOnlyIds()) {
// override to just select the Id values
query.fetch(many.getName(), 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.isLoggable(Level.FINE)) {
logger.fine("BeanCollection after load was empty. Owner:" + batch.get(i).getOwnerBean());
}
} else if (loadRequest.isLoadCache()) {
Object parentId = desc.getId(bc.getOwnerBean());
desc.cachePutMany(many, bc, parentId);
}
}
}
public void loadMany(BeanCollection<?> bc, LoadManyContext ctx, boolean onlyIds) {
Object parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
if (server.getAdminLogging().isDebugLazyLoad()) {
Class<?> cls = parentBean.getClass();
BeanDescriptor<?> desc = server.getBeanDescriptor(cls);
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) desc.getBeanProperty(propertyName);
StackTraceElement cause = debugLazyLoad.getStackTraceElement(cls);
String msg = "debug.lazyLoad " + many.getManyType() + " [" + desc + "][" + propertyName + "]";
if (cause != null) {
msg += " at: " + cause;
}
System.err.println(msg);
}
}
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, null, false);
}
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
boolean vanilla = (parentBean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
BeanCollection<?> beanCollection = null;
ExpressionList<?> filterMany = null;
if (!vanilla) {
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
Object currentValue = many.getValueUnderlying(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 = !vanilla && beanCollection != null && parentDesc.cacheIsUseManyId();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly, false)) {
return;
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(parentDesc.getBeanType());
if (refresh) {
// populate a new collection
Object emptyCollection = many.createEmpty(vanilla);
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);
query.setVanillaMode(vanilla);
if (ebi != null) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
}
server.findUnique(query, t);
if (beanCollection != null) {
if (beanCollection.checkEmptyLazyLoad()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean());
}
} else if (useManyIdCache) {
parentDesc.cachePutMany(many, beanCollection, parentId);
}
}
}
/**
* Load a batch of beans for +query or +lazy loading.
*/
public void loadBean(LoadBeanRequest loadRequest) {
List<EntityBeanIntercept> batch = loadRequest.getBatch();
if (batch.isEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
LoadBeanContext ctx = loadRequest.getLoadContext();
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
Class<?> beanType = desc.getBeanType();
EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]);
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
for (int i = 0; i < batch.size(); i++) {
EntityBeanIntercept ebi = batch.get(i);
Object 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();
// query the database
for (int i = 0; i < ebis.length; i++) {
Object parentBean = ebis[i].getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
persistenceContext.put(parentId, parentBean);
}
}
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());
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.cachePutBeanData(list.get(i));
}
}
for (int i = 0; i < ebis.length; i++) {
if (ebis[i].isReference()) {
// The underlying row in DB was deleted. Mark this bean as 'failed'
// but allow processing to continue until it is accessed by client code
ebis[i].setLazyLoadFailure();
}
}
}
public void refresh(Object bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
}
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
boolean vanilla = (bean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
if (!vanilla) {
ebi = ((EntityBean) bean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
Object id = desc.getId(bean);
if (pc == null) {
// a reference with no existing persistenceContext
pc = new DefaultPersistenceContext();
pc.put(id, bean);
if (ebi != null) {
ebi.setPersistenceContext(pc);
}
}
if (ebi != null) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.loadFromCache(bean, ebi, id)) {
return;
}
}
if (desc.lazyLoadMany(ebi)) {
return;
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
if (ebi != null) {
Object parentBean = ebi.getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
pc.putIfAbsent(parentId, parentBean);
}
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
// 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);
// make sure the query doesn't use the cache
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
query.setUseCache(false);
}
query.setVanillaMode(vanilla);
if (ebi != null && ebi.isReadOnly()) {
query.setReadOnly(true);
}
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);
}
}
}
package com.avaje.ebeaninternal.server.core;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityNotFoundException;
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.LoadBeanContext;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
import com.avaje.ebeaninternal.api.LoadManyContext;
import com.avaje.ebeaninternal.api.LoadManyRequest;
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.transaction.DefaultPersistenceContext;
/**
* Helper to handle lazy loading and refreshing of beans.
*
* @author rbygrave
*/
public class DefaultBeanLoader {
private static final Logger logger = Logger.getLogger(DefaultBeanLoader.class.getName());
private final DebugLazyLoad debugLazyLoad;
private final DefaultServer server;
protected DefaultBeanLoader(DefaultServer server, DebugLazyLoad debugLazyLoad) {
this.server = server;
this.debugLazyLoad = debugLazyLoad;
}
/**
* Return a batch size that might be less than the requestedBatchSize.
* <p>
* This means we can have large and variable requestedBatchSizes.
* </p>
* <p>
* 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.
* </p>
*/
private int getBatchSize(int batchListSize, int requestedBatchSize) {
if (batchListSize == requestedBatchSize) {
return batchListSize;
}
if (batchListSize == 1) {
// there is only one bean/collection to load
return 1;
}
if (requestedBatchSize <= 5) {
// anything less than 5 becomes 5
return 5;
}
if (batchListSize <= 10 || requestedBatchSize <= 10) {
// 10 or less to load
// ... or we wanted a batch size between 6 and 10
return 10;
}
if (batchListSize <= 20 || requestedBatchSize <= 20) {
// 20 or less to load
// ... or we wanted a batch size between 11 and 20
return 20;
}
if (batchListSize <= 50) {
return 50;
}
return requestedBatchSize;
}
public void refreshMany(Object parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void loadMany(LoadManyRequest loadRequest) {
List<BeanCollection<?>> batch = loadRequest.getBatch();
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
LoadManyContext ctx = loadRequest.getLoadContext();
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
PersistenceContext pc = ctx.getPersistenceContext();
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
for (int i = 0; i < batch.size(); i++) {
BeanCollection<?> bc = batch.get(i);
Object 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();
String idProperty = desc.getIdBinder().getIdProperty();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
query.setMode(Mode.LAZYLOAD_MANY);
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
query.select(idProperty);
query.fetch(many.getName());
if (idList.size() == 1) {
query.where().idEq(idList.get(0));
} else {
query.where().idIn(idList);
}
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
// potentially changes the joins and selected properties
ctx.configureQuery(query);
if (loadRequest.isOnlyIds()) {
// override to just select the Id values
query.fetch(many.getName(), 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.isLoggable(Level.FINE)) {
logger.fine("BeanCollection after load was empty. Owner:" + batch.get(i).getOwnerBean());
}
} else if (loadRequest.isLoadCache()) {
Object parentId = desc.getId(bc.getOwnerBean());
desc.cachePutMany(many, bc, parentId);
}
}
}
public void loadMany(BeanCollection<?> bc, LoadManyContext ctx, boolean onlyIds) {
Object parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
if (server.getAdminLogging().isDebugLazyLoad()) {
Class<?> cls = parentBean.getClass();
BeanDescriptor<?> desc = server.getBeanDescriptor(cls);
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) desc.getBeanProperty(propertyName);
StackTraceElement cause = debugLazyLoad.getStackTraceElement(cls);
String msg = "debug.lazyLoad " + many.getManyType() + " [" + desc + "][" + propertyName + "]";
if (cause != null) {
msg += " at: " + cause;
}
System.err.println(msg);
}
}
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, null, false);
}
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
boolean vanilla = (parentBean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
BeanCollection<?> beanCollection = null;
ExpressionList<?> filterMany = null;
if (!vanilla) {
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
Object currentValue = many.getValueUnderlying(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 = !vanilla && beanCollection != null && parentDesc.cacheIsUseManyId();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly, false)) {
return;
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(parentDesc.getBeanType());
if (refresh) {
// populate a new collection
Object emptyCollection = many.createEmpty(vanilla);
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);
query.setVanillaMode(vanilla);
if (ebi != null) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
}
server.findUnique(query, t);
if (beanCollection != null) {
if (beanCollection.checkEmptyLazyLoad()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean());
}
} else if (useManyIdCache) {
parentDesc.cachePutMany(many, beanCollection, parentId);
}
}
}
/**
* Load a batch of beans for +query or +lazy loading.
*/
public void loadBean(LoadBeanRequest loadRequest) {
List<EntityBeanIntercept> batch = loadRequest.getBatch();
if (batch.isEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
LoadBeanContext ctx = loadRequest.getLoadContext();
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
Class<?> beanType = desc.getBeanType();
EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]);
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
for (int i = 0; i < batch.size(); i++) {
EntityBeanIntercept ebi = batch.get(i);
Object 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();
// query the database
for (int i = 0; i < ebis.length; i++) {
Object parentBean = ebis[i].getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
persistenceContext.put(parentId, parentBean);
}
}
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());
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.cachePutBeanData(list.get(i));
}
}
for (int i = 0; i < ebis.length; i++) {
if (ebis[i].isReference()) {
// The underlying row in DB was deleted. Mark this bean as 'failed'
// but allow processing to continue until it is accessed by client code
ebis[i].setLazyLoadFailure();
}
}
}
public void refresh(Object bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
}
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
boolean vanilla = (bean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
if (!vanilla) {
ebi = ((EntityBean) bean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
Object id = desc.getId(bean);
if (pc == null) {
// a reference with no existing persistenceContext
pc = new DefaultPersistenceContext();
pc.put(id, bean);
if (ebi != null) {
ebi.setPersistenceContext(pc);
}
}
if (ebi != null) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.loadFromCache(bean, ebi, id)) {
return;
}
}
if (desc.lazyLoadMany(ebi)) {
return;
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
if (ebi != null) {
Object parentBean = ebi.getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
pc.putIfAbsent(parentId, parentBean);
}
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
// 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);
// make sure the query doesn't use the cache
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
query.setUseCache(false);
}
query.setVanillaMode(vanilla);
if (ebi != null && ebi.isReadOnly()) {
query.setReadOnly(true);
}
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);
}
}
}
@@ -1,143 +1,124 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.io.Serializable;
import java.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.SpiCallableSql;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.api.BindParams.Param;
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 TransactionEventTable transactionEvent = new TransactionEventTable();
private 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.SpiCallableSql;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.api.BindParams.Param;
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 TransactionEventTable transactionEvent = new TransactionEventTable();
private 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;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,246 +1,227 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.io.Serializable;
import 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.
* <p>
* There is also {@link Update} which is similar except should use logical bean and
* property names rather than physical table and column names.
* </p>
* <p>
* SqlUpdate is designed for general DML sql and CallableSql is
* designed for use with stored procedures.
* </p>
*
* <pre class="code">
* // String sql = &quot;update f_topic set post_count = :count where id = :topicId&quot;;
*
* SqlUpdate update = new SqlUpdate(sql);
* update.setParameter(&quot;count&quot;, 1);
* update.setParameter(&quot;topicId&quot;, 50);
*
* int modifiedCount = Ebean.execute(update);
* </pre>
*
* <p>
* 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 <em>isAutoTableMod()</em> 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.
* </p>
* <p>
* You can sql.setAutoTableMod(false); to stop the automatic table modification
* </p>
* <p>
* EXAMPLE: Using JDBC batching with SqlUpdate
* </p>
* <pre class="code">
*
* String data = &quot;This is a simple test of the batch processing&quot;
* + &quot; mode and the transaction execute batch method&quot;;
*
* String[] da = data.split(&quot; &quot;);
*
* String sql = &quot;insert into junk (word) values (?)&quot;;
*
* SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
*
* Transaction t = Ebean.beginTransaction();
* t.setBatchMode(true);
* t.setBatchSize(3);
* try {
* for (int i = 0; i &lt; da.length; i++) {
*
* sqlUpdate.setParameter(1, da[i]);
* sqlUpdate.execute();
* }
*
* // NB: commit implicitly flushes the batch
* Ebean.commitTransaction();
*
* } finally {
* Ebean.endTransaction();
* }
* </pre>
* @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;
/**
* 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.
* <p>
* Useful if you are building the sql and binding parameters at the
* same time.
* </p>
*/
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 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.
* <p>
* There is also {@link Update} which is similar except should use logical bean and
* property names rather than physical table and column names.
* </p>
* <p>
* SqlUpdate is designed for general DML sql and CallableSql is
* designed for use with stored procedures.
* </p>
*
* <pre class="code">
* // String sql = &quot;update f_topic set post_count = :count where id = :topicId&quot;;
*
* SqlUpdate update = new SqlUpdate(sql);
* update.setParameter(&quot;count&quot;, 1);
* update.setParameter(&quot;topicId&quot;, 50);
*
* int modifiedCount = Ebean.execute(update);
* </pre>
*
* <p>
* 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 <em>isAutoTableMod()</em> 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.
* </p>
* <p>
* You can sql.setAutoTableMod(false); to stop the automatic table modification
* </p>
* <p>
* EXAMPLE: Using JDBC batching with SqlUpdate
* </p>
* <pre class="code">
*
* String data = &quot;This is a simple test of the batch processing&quot;
* + &quot; mode and the transaction execute batch method&quot;;
*
* String[] da = data.split(&quot; &quot;);
*
* String sql = &quot;insert into junk (word) values (?)&quot;;
*
* SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
*
* Transaction t = Ebean.beginTransaction();
* t.setBatchMode(true);
* t.setBatchSize(3);
* try {
* for (int i = 0; i &lt; da.length; i++) {
*
* sqlUpdate.setParameter(1, da[i]);
* sqlUpdate.execute();
* }
*
* // NB: commit implicitly flushes the batch
* Ebean.commitTransaction();
*
* } finally {
* Ebean.endTransaction();
* }
* </pre>
* @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;
/**
* 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.
* <p>
* Useful if you are building the sql and binding parameters at the
* same time.
* </p>
*/
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 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;
}
}
@@ -1,177 +1,158 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.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.
* <p>
* This intentionally does not include any OneToMany or ManyToMany properties.
* </p>
*/
public class DiffHelp {
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the
* 'OldValues' of a is used to compare with (as B).
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany
* properties.
* </p>
*/
public Map<String, ValuePair> diff(Object a, Object b, BeanDescriptor<?> desc) {
boolean oldValues = false;
if (b == null) {
// get the old values from a
if (a instanceof EntityBean) {
EntityBean eb = (EntityBean) a;
b = eb._ebean_getIntercept().getOldValues();
oldValues = true;
}
}
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
if (b == null) {
return map;
}
// check the simple properties
BeanProperty[] base = desc.propertiesBaseScalar();
for (int i = 0; i < base.length; i++) {
Object aval = base[i].getValue(a);
Object bval = base[i].getValue(b);
if (!ValueUtil.areEqual(aval, bval)) {
map.put(base[i].getName(), new ValuePair(aval, bval));
}
}
diffAssocOne(a, b, desc, map);
diffEmbedded(a, b, desc, map, oldValues);
return map;
}
/**
* Check the Embedded bean properties for differences.
* <p>
* If ANY of the properties are different then the whole Embedded bean is
* determined to be different as is added to the map.
* </p>
*/
private void diffEmbedded(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map,
boolean oldValues) {
BeanPropertyAssocOne<?>[] emb = desc.propertiesEmbedded();
for (int i = 0; i < emb.length; i++) {
Object aval = emb[i].getValue(a);
Object bval = emb[i].getValue(b);
if (oldValues) {
bval = ((EntityBean) bval)._ebean_getIntercept().getOldValues();
if (bval == null) {
continue;
}
}
if (!isBothNull(aval, bval)) {
if (isDiffNull(aval, bval)) {
// one of the embedded beans is null
map.put(emb[i].getName(), new ValuePair(aval, bval));
} else {
// if ANY of the properties in an Embedded bean is
// different, treat the whole bean as being different
BeanProperty[] props = emb[i].getProperties();
for (int j = 0; j < props.length; j++) {
Object aEmbPropVal = props[j].getValue(aval);
Object bEmbPropVal = props[j].getValue(bval);
if (!ValueUtil.areEqual(aEmbPropVal, bEmbPropVal)) {
// if one prop is different put the
// embedded bean in the map
map.put(emb[i].getName(), new ValuePair(aval, bval));
}
}
}
}
}
}
/**
* 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(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> 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)) {
if (isDiffNull(aval, bval)) {
// one of them is/was null
map.put(ones[i].getName(), new ValuePair(aval, bval));
} else {
// check to see if the Id properties
// are different
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
Object aOneId = oneDesc.getId(aval);
Object bOneId = oneDesc.getId(bval);
if (!ValueUtil.areEqual(aOneId, bOneId)) {
// the ids are different
map.put(ones[i].getName(), 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.
* <p>
* This intentionally does not include any OneToMany or ManyToMany properties.
* </p>
*/
public class DiffHelp {
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the
* 'OldValues' of a is used to compare with (as B).
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany
* properties.
* </p>
*/
public Map<String, ValuePair> diff(Object a, Object b, BeanDescriptor<?> desc) {
boolean oldValues = false;
if (b == null) {
// get the old values from a
if (a instanceof EntityBean) {
EntityBean eb = (EntityBean) a;
b = eb._ebean_getIntercept().getOldValues();
oldValues = true;
}
}
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
if (b == null) {
return map;
}
// check the simple properties
BeanProperty[] base = desc.propertiesBaseScalar();
for (int i = 0; i < base.length; i++) {
Object aval = base[i].getValue(a);
Object bval = base[i].getValue(b);
if (!ValueUtil.areEqual(aval, bval)) {
map.put(base[i].getName(), new ValuePair(aval, bval));
}
}
diffAssocOne(a, b, desc, map);
diffEmbedded(a, b, desc, map, oldValues);
return map;
}
/**
* Check the Embedded bean properties for differences.
* <p>
* If ANY of the properties are different then the whole Embedded bean is
* determined to be different as is added to the map.
* </p>
*/
private void diffEmbedded(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map,
boolean oldValues) {
BeanPropertyAssocOne<?>[] emb = desc.propertiesEmbedded();
for (int i = 0; i < emb.length; i++) {
Object aval = emb[i].getValue(a);
Object bval = emb[i].getValue(b);
if (oldValues) {
bval = ((EntityBean) bval)._ebean_getIntercept().getOldValues();
if (bval == null) {
continue;
}
}
if (!isBothNull(aval, bval)) {
if (isDiffNull(aval, bval)) {
// one of the embedded beans is null
map.put(emb[i].getName(), new ValuePair(aval, bval));
} else {
// if ANY of the properties in an Embedded bean is
// different, treat the whole bean as being different
BeanProperty[] props = emb[i].getProperties();
for (int j = 0; j < props.length; j++) {
Object aEmbPropVal = props[j].getValue(aval);
Object bEmbPropVal = props[j].getValue(bval);
if (!ValueUtil.areEqual(aEmbPropVal, bEmbPropVal)) {
// if one prop is different put the
// embedded bean in the map
map.put(emb[i].getName(), new ValuePair(aval, bval));
}
}
}
}
}
}
/**
* 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(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> 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)) {
if (isDiffNull(aval, bval)) {
// one of them is/was null
map.put(ones[i].getName(), new ValuePair(aval, bval));
} else {
// check to see if the Id properties
// are different
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
Object aOneId = oneDesc.getId(aval);
Object bOneId = oneDesc.getId(bval);
if (!ValueUtil.areEqual(aOneId, bOneId)) {
// the ids are different
map.put(ones[i].getName(), 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;
}
}
}
@@ -1,295 +1,276 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.logging.Logger;
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.config.ldap.LdapConfig;
import com.avaje.ebean.config.ldap.LdapContextFactory;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonValueAdapter;
import com.avaje.ebeaninternal.api.ClassUtil;
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.jmx.MAdminLogging;
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.subclass.SubClassManager;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
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;
/**
* 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 = Logger.getLogger(InternalConfiguration.class.getName());
private final ServerConfig serverConfig;
private final BootupClasses bootupClasses;
private final SubClassManager subClassManager;
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 MAdminLogging logControl;
private final DebugLazyLoad debugLazyLoad;
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;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, ServerCacheManager cacheManager,
SpiBackgroundExecutor backgroundExecutor, ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
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.subClassManager = new SubClassManager(serverConfig);
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.debugLazyLoad = new DebugLazyLoad(serverConfig.isDebugLazyLoad());
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager,
this.getBootupClasses());
this.logControl = new MAdminLogging(serverConfig, transactionManager);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), logControl, binder, backgroundExecutor);
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);
}
}
public JsonContext createJsonContext(SpiEbeanServer server) {
String s = serverConfig.getProperty("json.pretty", "false");
boolean dfltPretty = "true".equalsIgnoreCase(s);
s = serverConfig.getProperty("json.jsonValueAdapter", null);
JsonValueAdapter va = new DefaultJsonValueAdapter();
if (s != null) {
va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass());
}
return new DJsonContext(server, va, dfltPretty);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(logControl, binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
LdapContextFactory ldapCtxFactory = null;
LdapConfig ldapConfig = serverConfig.getLdapConfig();
if (ldapConfig != null) {
ldapCtxFactory = ldapConfig.getContextFactory();
}
return new DefaultPersister(server, serverConfig.isValidateOnSave(), binder, beanDescriptorManager, pstmtBatch, ldapCtxFactory);
}
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 SubClassManager getSubClassManager() {
return subClassManager;
}
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 MAdminLogging getLogControl() {
return logControl;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public TransactionScopeManager getTransactionScopeManager() {
return transactionScopeManager;
}
public CQueryEngine getCQueryEngine() {
return cQueryEngine;
}
public ClusterManager getClusterManager() {
return clusterManager;
}
public DebugLazyLoad getDebugLazyLoad() {
return debugLazyLoad;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
}
package com.avaje.ebeaninternal.server.core;
import java.util.logging.Logger;
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.config.ldap.LdapConfig;
import com.avaje.ebean.config.ldap.LdapContextFactory;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonValueAdapter;
import com.avaje.ebeaninternal.api.ClassUtil;
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.jmx.MAdminLogging;
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.subclass.SubClassManager;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
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;
/**
* 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 = Logger.getLogger(InternalConfiguration.class.getName());
private final ServerConfig serverConfig;
private final BootupClasses bootupClasses;
private final SubClassManager subClassManager;
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 MAdminLogging logControl;
private final DebugLazyLoad debugLazyLoad;
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;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, ServerCacheManager cacheManager,
SpiBackgroundExecutor backgroundExecutor, ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
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.subClassManager = new SubClassManager(serverConfig);
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.debugLazyLoad = new DebugLazyLoad(serverConfig.isDebugLazyLoad());
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager,
this.getBootupClasses());
this.logControl = new MAdminLogging(serverConfig, transactionManager);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), logControl, binder, backgroundExecutor);
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);
}
}
public JsonContext createJsonContext(SpiEbeanServer server) {
String s = serverConfig.getProperty("json.pretty", "false");
boolean dfltPretty = "true".equalsIgnoreCase(s);
s = serverConfig.getProperty("json.jsonValueAdapter", null);
JsonValueAdapter va = new DefaultJsonValueAdapter();
if (s != null) {
va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass());
}
return new DJsonContext(server, va, dfltPretty);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(logControl, binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
LdapContextFactory ldapCtxFactory = null;
LdapConfig ldapConfig = serverConfig.getLdapConfig();
if (ldapConfig != null) {
ldapCtxFactory = ldapConfig.getContextFactory();
}
return new DefaultPersister(server, serverConfig.isValidateOnSave(), binder, beanDescriptorManager, pstmtBatch, ldapCtxFactory);
}
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 SubClassManager getSubClassManager() {
return subClassManager;
}
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 MAdminLogging getLogControl() {
return logControl;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public TransactionScopeManager getTransactionScopeManager() {
return transactionScopeManager;
}
public CQueryEngine getCQueryEngine() {
return cQueryEngine;
}
public ClusterManager getClusterManager() {
return clusterManager;
}
public DebugLazyLoad getDebugLazyLoad() {
return debugLazyLoad;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
}
@@ -1,68 +1,49 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.config.GlobalProperties;
/**
* Helper to lookup a DataSource from JNDI.
*/
public class JndiDataSourceLookup {
private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/";
String jndiPrefix = GlobalProperties.get("ebean.datasource.jndi.prefix", DEFAULT_PREFIX);
public JndiDataSourceLookup() {
}
/**
* Return the DataSource by JNDI lookup.
* <p>
* If name is null the 'default' dataSource is returned.
* </p>
*/
public DataSource lookup(String jndiName) {
try {
if (!jndiName.startsWith("java:")){
jndiName = jndiPrefix + 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;
import com.avaje.ebean.config.GlobalProperties;
/**
* Helper to lookup a DataSource from JNDI.
*/
public class JndiDataSourceLookup {
private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/";
String jndiPrefix = GlobalProperties.get("ebean.datasource.jndi.prefix", DEFAULT_PREFIX);
public JndiDataSourceLookup() {
}
/**
* Return the DataSource by JNDI lookup.
* <p>
* If name is null the 'default' dataSource is returned.
* </p>
*/
public DataSource lookup(String jndiName) {
try {
if (!jndiName.startsWith("java:")){
jndiName = jndiPrefix + 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);
}
}
}
@@ -1,83 +1,64 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.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;
}
}
}
@@ -1,41 +1,22 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import 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;
}
}
@@ -1,57 +1,38 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import 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> T findId(OrmQueryRequest<T> request);
/**
* Execute the findList, findSet, findMap query returning an appropriate BeanCollection.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
/**
* Execute the query using a QueryIterator.
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request);
/**
* Execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request);
/**
* Execute the find id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> 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> T findId(OrmQueryRequest<T> request);
/**
* Execute the findList, findSet, findMap query returning an appropriate BeanCollection.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
/**
* Execute the query using a QueryIterator.
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request);
/**
* Execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request);
/**
* Execute the find id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request);
}
@@ -1,429 +1,410 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.RawSql;
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.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;
/**
* Wraps the objects involved in executing a Query.
*/
public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRequest<T>, SpiOrmQueryRequest<T> {
private final BeanDescriptor<T> beanDescriptor;
private final OrmQueryEngine queryEngine;
private final SpiQuery<T> query;
private final boolean vanillaMode;
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private final Boolean readOnly;
private final RawSql rawSql;
private PersistenceContext persistenceContext;
private Integer cacheKey;
private int queryPlanHash;
/**
* Flag set if background fetching taking place. In this case the transaction
* is rolled back by the background fetching thread. Background fetching
* always takes place in its own transaction.
*/
private boolean backgroundFetching;
/**
* Create the InternalQueryRequest.
*/
public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery<T> query, BeanDescriptor<T> desc, SpiTransaction t) {
super(server, t);
this.beanDescriptor = desc;
this.rawSql = query.getRawSql();
this.finder = beanDescriptor.getBeanFinder();
this.queryEngine = queryEngine;
this.query = query;
this.vanillaMode = query.isVanillaMode(server.isVanillaMode());
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
}
public void setTotalHits(int totalHits) {
query.setTotalHits(totalHits);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
}
/**
* For use with QueryIterator and secondary queries this returns the minimum
* batch size that should be loaded before executing the secondary queries.
* <p>
* If -1 is returned then NO secondary queries are registered and simple
* iteration is fine.
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return graphContext.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<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* Return the graph context for this query.
*/
public LoadContext getGraphContext() {
return graphContext;
}
/**
* 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.
* <p>
* 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.
* </p>
*/
@Override
public void initTransIfRequired() {
// first check if the query requires its own transaction
if (query.createOwnTransaction()) {
// using background fetch or query listener etc
transaction = ebeanServer.createQueryTransaction();
createdTransaction = true;
} else 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;
}
}
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
*/
private PersistenceContext getPersistenceContext(SpiQuery<?> query, SpiTransaction t) {
PersistenceContext ctx = query.getPersistenceContext();
if (ctx == null) {
ctx = t.getPersistenceContext();
}
return ctx;
}
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
public void endTransIfRequired() {
if (createdTransaction && !backgroundFetching) {
// we can rollback as readOnly transaction
transaction.rollback();
}
}
/**
* This query is using background fetching.
*/
public void setBackgroundFetching() {
backgroundFetching = true;
}
/**
* Return true if this is a find by id (rather than List Set or Map).
*/
public boolean isFindById() {
return query.getType() == Type.BEAN;
}
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Execute the query as findById.
*/
public Object findId() {
return queryEngine.findId(this);
}
public int findRowCount() {
return queryEngine.findRowCount(this);
}
public List<Object> findIds() {
BeanIdList idList = queryEngine.findIds(this);
return idList.getIdList();
}
public void findVisit(QueryResultVisitor<T> visitor) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
while (it.hasNext()) {
if (!visitor.accept(it.next())) {
break;
}
}
} finally {
it.close();
}
}
public QueryIterator<T> findIterate() {
return queryEngine.findIterate(this);
}
/**
* Execute the query as findList.
*/
@SuppressWarnings("unchecked")
public List<T> findList() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (List<T>) (vanillaMode ? bc.getActualCollection() : bc);
}
/**
* Execute the query as findSet.
*/
@SuppressWarnings("unchecked")
public Set<?> findSet() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (Set<T>) (vanillaMode ? bc.getActualCollection() : bc);
}
/**
* Execute the query as findMap.
*/
public Map<?, ?> findMap() {
String mapKey = query.getMapKey();
if (mapKey == null) {
BeanProperty[] ids = beanDescriptor.propertiesId();
if (ids.length == 1) {
query.setMapKey(ids[0].getName());
} else {
String msg = "No mapKey specified for query";
throw new PersistenceException(msg);
}
}
BeanCollection<T> bc = queryEngine.findMany(this);
return (Map<?, ?>) (vanillaMode ? bc.getActualCollection() : bc);
}
public SpiQuery.Type getQueryType() {
return query.getType();
}
/**
* Return a bean specific finder if one has been set.
*/
public BeanFinder<T> getBeanFinder() {
return finder;
}
/**
* Return the find that is to be performed.
*/
public SpiQuery<T> 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.
* <p>
* 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.
* </p>
*/
public int 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<T> getFromQueryCache() {
if (!query.isUseQueryCache()) {
return null;
}
if (query.getType() == null) {
// the query plan and bind values must be the same
cacheKey = Integer.valueOf(query.queryHash());
} else {
// additionally the return type (List/Set/Map) must be the same
cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
}
// TODO: Sort out returning BeanCollection from L2 cache
return null;
// BeanCollection<T> bc = beanDescriptor.queryCacheGet(cacheKey);
// if (bc != null && Boolean.FALSE.equals(query.isReadOnly())) {
// // Explicit readOnly=false for query cache
// CopyContext ctx = new CopyContext(vanillaMode, false);
// return new CopyBeanCollection<T>(bc, beanDescriptor, ctx, 5).copy();
// }
// return bc;
}
public void putToQueryCache(BeanCollection<T> 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) {
if (transaction.isLogSql()) {
transaction.logInternal(sql);
}
}
}
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.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.RawSql;
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.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;
/**
* Wraps the objects involved in executing a Query.
*/
public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRequest<T>, SpiOrmQueryRequest<T> {
private final BeanDescriptor<T> beanDescriptor;
private final OrmQueryEngine queryEngine;
private final SpiQuery<T> query;
private final boolean vanillaMode;
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private final Boolean readOnly;
private final RawSql rawSql;
private PersistenceContext persistenceContext;
private Integer cacheKey;
private int queryPlanHash;
/**
* Flag set if background fetching taking place. In this case the transaction
* is rolled back by the background fetching thread. Background fetching
* always takes place in its own transaction.
*/
private boolean backgroundFetching;
/**
* Create the InternalQueryRequest.
*/
public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery<T> query, BeanDescriptor<T> desc, SpiTransaction t) {
super(server, t);
this.beanDescriptor = desc;
this.rawSql = query.getRawSql();
this.finder = beanDescriptor.getBeanFinder();
this.queryEngine = queryEngine;
this.query = query;
this.vanillaMode = query.isVanillaMode(server.isVanillaMode());
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
}
public void setTotalHits(int totalHits) {
query.setTotalHits(totalHits);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
}
/**
* For use with QueryIterator and secondary queries this returns the minimum
* batch size that should be loaded before executing the secondary queries.
* <p>
* If -1 is returned then NO secondary queries are registered and simple
* iteration is fine.
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return graphContext.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<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* Return the graph context for this query.
*/
public LoadContext getGraphContext() {
return graphContext;
}
/**
* 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.
* <p>
* 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.
* </p>
*/
@Override
public void initTransIfRequired() {
// first check if the query requires its own transaction
if (query.createOwnTransaction()) {
// using background fetch or query listener etc
transaction = ebeanServer.createQueryTransaction();
createdTransaction = true;
} else 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;
}
}
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
*/
private PersistenceContext getPersistenceContext(SpiQuery<?> query, SpiTransaction t) {
PersistenceContext ctx = query.getPersistenceContext();
if (ctx == null) {
ctx = t.getPersistenceContext();
}
return ctx;
}
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
public void endTransIfRequired() {
if (createdTransaction && !backgroundFetching) {
// we can rollback as readOnly transaction
transaction.rollback();
}
}
/**
* This query is using background fetching.
*/
public void setBackgroundFetching() {
backgroundFetching = true;
}
/**
* Return true if this is a find by id (rather than List Set or Map).
*/
public boolean isFindById() {
return query.getType() == Type.BEAN;
}
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Execute the query as findById.
*/
public Object findId() {
return queryEngine.findId(this);
}
public int findRowCount() {
return queryEngine.findRowCount(this);
}
public List<Object> findIds() {
BeanIdList idList = queryEngine.findIds(this);
return idList.getIdList();
}
public void findVisit(QueryResultVisitor<T> visitor) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
while (it.hasNext()) {
if (!visitor.accept(it.next())) {
break;
}
}
} finally {
it.close();
}
}
public QueryIterator<T> findIterate() {
return queryEngine.findIterate(this);
}
/**
* Execute the query as findList.
*/
@SuppressWarnings("unchecked")
public List<T> findList() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (List<T>) (vanillaMode ? bc.getActualCollection() : bc);
}
/**
* Execute the query as findSet.
*/
@SuppressWarnings("unchecked")
public Set<?> findSet() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (Set<T>) (vanillaMode ? bc.getActualCollection() : bc);
}
/**
* Execute the query as findMap.
*/
public Map<?, ?> findMap() {
String mapKey = query.getMapKey();
if (mapKey == null) {
BeanProperty[] ids = beanDescriptor.propertiesId();
if (ids.length == 1) {
query.setMapKey(ids[0].getName());
} else {
String msg = "No mapKey specified for query";
throw new PersistenceException(msg);
}
}
BeanCollection<T> bc = queryEngine.findMany(this);
return (Map<?, ?>) (vanillaMode ? bc.getActualCollection() : bc);
}
public SpiQuery.Type getQueryType() {
return query.getType();
}
/**
* Return a bean specific finder if one has been set.
*/
public BeanFinder<T> getBeanFinder() {
return finder;
}
/**
* Return the find that is to be performed.
*/
public SpiQuery<T> 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.
* <p>
* 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.
* </p>
*/
public int 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<T> getFromQueryCache() {
if (!query.isUseQueryCache()) {
return null;
}
if (query.getType() == null) {
// the query plan and bind values must be the same
cacheKey = Integer.valueOf(query.queryHash());
} else {
// additionally the return type (List/Set/Map) must be the same
cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
}
// TODO: Sort out returning BeanCollection from L2 cache
return null;
// BeanCollection<T> bc = beanDescriptor.queryCacheGet(cacheKey);
// if (bc != null && Boolean.FALSE.equals(query.isReadOnly())) {
// // Explicit readOnly=false for query cache
// CopyContext ctx = new CopyContext(vanillaMode, false);
// return new CopyBeanCollection<T>(bc, beanDescriptor, ctx, 5).copy();
// }
// return bc;
}
public void putToQueryCache(BeanCollection<T> 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) {
if (transaction.isLogSql()) {
transaction.logInternal(sql);
}
}
}
@@ -1,127 +1,108 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import 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 {
INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
};
boolean persistCascade;
/**
* One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL.
*/
Type type;
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();
}
/**
* Execute the Callable statement.
*/
public int executeStatement() {
boolean batch = transaction.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, batch);
} 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;
}
/**
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
* CALLABLESQL.
*/
public void setType(Type type) {
this.type = 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 {
INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
};
boolean persistCascade;
/**
* One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL.
*/
Type type;
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();
}
/**
* Execute the Callable statement.
*/
public int executeStatement() {
boolean batch = transaction.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, batch);
} 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;
}
/**
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
* CALLABLESQL.
*/
public void setType(Type type) {
this.type = type;
}
/**
* Return true if save and delete should cascade.
*/
public boolean isPersistCascade() {
return persistCascade;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,171 +1,152 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.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.SpiCallableSql;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.api.BindParams.Param;
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) {
}
/**
* False for CallableSql.
*/
public boolean useGeneratedKeys() {
return false;
}
/**
* 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.logInternal(m);
}
// register table modifications with the transaction event
TransactionEventTable tableEvents = callableSql.getTransactionEventTable();
if (tableEvents != null && !tableEvents.isEmpty()) {
transaction.getEvent().add(tableEvents);
}
}
/**
* 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<Param> list = bindParam.positionedParameters();
int pos = 0;
for (int i = 0; i < list.size(); i++) {
pos++;
BindParams.Param param = (BindParams.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.SpiCallableSql;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.api.BindParams.Param;
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) {
}
/**
* False for CallableSql.
*/
public boolean useGeneratedKeys() {
return false;
}
/**
* 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.logInternal(m);
}
// register table modifications with the transaction event
TransactionEventTable tableEvents = callableSql.getTransactionEventTable();
if (tableEvents != null && !tableEvents.isEmpty()) {
transaction.getEvent().add(tableEvents);
}
}
/**
* 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<Param> list = bindParam.positionedParameters();
int pos = 0;
for (int i = 0; i < list.size(); i++) {
pos++;
BindParams.Param param = (BindParams.Param) list.get(i);
if (param.isOutParam()) {
Object outValue = cstmt.getObject(pos);
param.setOutValue(outValue);
}
}
}
}
@@ -1,138 +1,119 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.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.logInternal(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.logInternal(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;
}
}
}
}
@@ -1,145 +1,126 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.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.logInternal(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.logInternal(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;
}
}
}
}
@@ -1,107 +1,88 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.Collection;
import java.util.Set;
import com.avaje.ebean.CallableSql;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.Update;
/**
* API for persisting a bean.
*/
public interface Persister {
/**
* Force an Update using the given bean.
*/
public void forceUpdate(Object entityBean, Set<String> updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties);
/**
* Force an Insert using the given bean.
*/
public void forceInsert(Object entityBean, Transaction t);
/**
* Insert or update the bean depending on its state.
*/
public void save(Object entityBean, Transaction t);
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
public void saveManyToManyAssociations(Object 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(Object 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(Object ownerBean, String propertyName, Transaction t);
/**
* Delete a bean given it's type and id value.
* <p>
* This will also cascade delete one level of children.
* </p>
*/
public int delete(Class<?> beanType, Object id, Transaction transaction);
/**
* Delete the bean.
*/
public void delete(Object 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 java.util.Set;
import com.avaje.ebean.CallableSql;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.Update;
/**
* API for persisting a bean.
*/
public interface Persister {
/**
* Force an Update using the given bean.
*/
public void forceUpdate(Object entityBean, Set<String> updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties);
/**
* Force an Insert using the given bean.
*/
public void forceInsert(Object entityBean, Transaction t);
/**
* Insert or update the bean depending on its state.
*/
public void save(Object entityBean, Transaction t);
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
public void saveManyToManyAssociations(Object 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(Object 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(Object ownerBean, String propertyName, Transaction t);
/**
* Delete a bean given it's type and id value.
* <p>
* This will also cascade delete one level of children.
* </p>
*/
public int delete(Class<?> beanType, Object id, Transaction transaction);
/**
* Delete the bean.
*/
public void delete(Object 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);
}
@@ -1,111 +1,92 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.bean.SerializeControl;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Read an ObjectInputStream potentially containing "proxy" / "subclassed"
* entity objects.
* <p>
* This does not need to be used for "Enhanced" beans... but if you want to
* deserialise "proxy" / "subclassed" beans you need to use this
* ProxyBeanObjectInputStream. The reason is because it is required to resolve
* the class (The class with the $$EntityBean suffix). As this class is in
* another class loader typically as plain ObjectInputStream is unable to resolve
* the class - and hence we need to use this ProxyBeanObjectInputStream.
* </p>
*/
public class ProxyBeanObjectInputStream extends ObjectInputStream {
private final SpiEbeanServer ebeanServer;
/**
* Create with a given InputStream and EbeanServer.
* <p>
* The EbeanServer should be the one that created the 'proxy' classes that
* were serialised.
* </p>
*/
public ProxyBeanObjectInputStream(InputStream in, EbeanServer ebeanServer)
throws IOException {
super(in);
this.ebeanServer = (SpiEbeanServer) ebeanServer;
SerializeControl.setVanilla(false);
}
/**
* close and reset the serialization mode.
* <p>
* uses SerializeControl.resetToDefault().
* </p>
*/
public void close() throws IOException {
super.close();
SerializeControl.resetToDefault();
}
/**
* Resolve the generated Class potentially using reading the embedded
* MethodInfo.
*/
protected Class<?> resolveGenerated(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
String className = desc.getName();
String vanillaClassName = SubClassUtil.getSuperClassName(className);
Class<?> vanillaClass = ClassUtil.forName(vanillaClassName, this.getClass());
BeanDescriptor<?> d = ebeanServer.getBeanDescriptor(vanillaClass);
if (d == null) {
String msg = "Could not find BeanDescriptor for "+ vanillaClassName;
throw new IOException(msg);
} else {
return d.getFactoryType();
}
}
/**
* checks for generated subclasses and handles them appropriately.
*/
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
String className = desc.getName();
if (SubClassUtil.isSubClass(className)) {
return resolveGenerated(desc);
}
return super.resolveClass(desc);
}
}
package com.avaje.ebeaninternal.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.bean.SerializeControl;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Read an ObjectInputStream potentially containing "proxy" / "subclassed"
* entity objects.
* <p>
* This does not need to be used for "Enhanced" beans... but if you want to
* deserialise "proxy" / "subclassed" beans you need to use this
* ProxyBeanObjectInputStream. The reason is because it is required to resolve
* the class (The class with the $$EntityBean suffix). As this class is in
* another class loader typically as plain ObjectInputStream is unable to resolve
* the class - and hence we need to use this ProxyBeanObjectInputStream.
* </p>
*/
public class ProxyBeanObjectInputStream extends ObjectInputStream {
private final SpiEbeanServer ebeanServer;
/**
* Create with a given InputStream and EbeanServer.
* <p>
* The EbeanServer should be the one that created the 'proxy' classes that
* were serialised.
* </p>
*/
public ProxyBeanObjectInputStream(InputStream in, EbeanServer ebeanServer)
throws IOException {
super(in);
this.ebeanServer = (SpiEbeanServer) ebeanServer;
SerializeControl.setVanilla(false);
}
/**
* close and reset the serialization mode.
* <p>
* uses SerializeControl.resetToDefault().
* </p>
*/
public void close() throws IOException {
super.close();
SerializeControl.resetToDefault();
}
/**
* Resolve the generated Class potentially using reading the embedded
* MethodInfo.
*/
protected Class<?> resolveGenerated(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
String className = desc.getName();
String vanillaClassName = SubClassUtil.getSuperClassName(className);
Class<?> vanillaClass = ClassUtil.forName(vanillaClassName, this.getClass());
BeanDescriptor<?> d = ebeanServer.getBeanDescriptor(vanillaClass);
if (d == null) {
String msg = "Could not find BeanDescriptor for "+ vanillaClassName;
throw new IOException(msg);
} else {
return d.getFactoryType();
}
}
/**
* checks for generated subclasses and handles them appropriately.
*/
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
String className = desc.getName();
if (SubClassUtil.isSubClass(className)) {
return resolveGenerated(desc);
}
return super.resolveClass(desc);
}
}
@@ -1,36 +1,17 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.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;
}
@@ -1,234 +1,215 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
/**
* Helper for performing a 'refresh' on an Entity bean.
* <p>
* Note that this does not 'refresh' any OnetoMany or ManyToMany properties. It
* refreshes all the other properties though.
* </p>
*/
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<String> 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<String> 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<String> 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.
* <p>
* Note that this does not 'refresh' any OnetoMany or ManyToMany properties. It
* refreshes all the other properties though.
* </p>
*/
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<String> 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<String> 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<String> 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);
// }
//
//
//
}
@@ -1,147 +1,128 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.List;
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;
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
trans.rollback();
}
}
/**
* 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);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
// trans.setReadOnly(true);
createdTransaction = true;
}
}
}
/**
* End the transaction if it was locally created.
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as a readOnly transaction.
trans.rollback();
}
}
@SuppressWarnings("unchecked")
public List<SqlRow> findList() {
queryType = SpiQuery.Type.LIST;
return (List<SqlRow>) queryEngine.findMany(this);
}
@SuppressWarnings("unchecked")
public Set<SqlRow> findSet() {
queryType = SpiQuery.Type.SET;
return (Set<SqlRow>) queryEngine.findMany(this);
}
@SuppressWarnings("unchecked")
public Map<?, SqlRow> findMap() {
queryType = SpiQuery.Type.MAP;
return (Map<?, SqlRow>) 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;
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
trans.rollback();
}
}
/**
* 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);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
// trans.setReadOnly(true);
createdTransaction = true;
}
}
}
/**
* End the transaction if it was locally created.
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as a readOnly transaction.
trans.rollback();
}
}
@SuppressWarnings("unchecked")
public List<SqlRow> findList() {
queryType = SpiQuery.Type.LIST;
return (List<SqlRow>) queryEngine.findMany(this);
}
@SuppressWarnings("unchecked")
public Set<SqlRow> findSet() {
queryType = SpiQuery.Type.SET;
return (Set<SqlRow>) queryEngine.findMany(this);
}
@SuppressWarnings("unchecked")
public Map<?, SqlRow> findMap() {
queryType = SpiQuery.Type.MAP;
return (Map<?, SqlRow>) 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();
}
}
@@ -1,76 +1,57 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
/**
* Listens for webserver server starting and stopping events.
*
* <p>
* Register this listener in the web.xml configuration file. This will listen
* for startup and shutdown events.
* </p>
*/
public class ServletContextListener implements javax.servlet.ServletContextListener {
private static final Logger logger = Logger.getLogger(ServletContextListener.class.getName());
/**
* The servlet container is stopping.
*/
public void contextDestroyed(ServletContextEvent event) {
ShutdownManager.shutdown();
}
/**
* The servlet container is starting.
* <p>
* Initialise the properties file using SystemProperties.initWebapp();
* and start Ebean.
* </p>
*/
public void contextInitialized(ServletContextEvent event) {
try {
ServletContext servletContext = event.getServletContext();
GlobalProperties.setServletContext(servletContext);
if (servletContext != null) {
String servletRealPath = servletContext.getRealPath("");
GlobalProperties.put("servlet.realpath", servletRealPath);
logger.info("servlet.realpath=[" + servletRealPath + "]");
}
Ebean.getServer(null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
package com.avaje.ebeaninternal.server.core;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
/**
* Listens for webserver server starting and stopping events.
*
* <p>
* Register this listener in the web.xml configuration file. This will listen
* for startup and shutdown events.
* </p>
*/
public class ServletContextListener implements javax.servlet.ServletContextListener {
private static final Logger logger = Logger.getLogger(ServletContextListener.class.getName());
/**
* The servlet container is stopping.
*/
public void contextDestroyed(ServletContextEvent event) {
ShutdownManager.shutdown();
}
/**
* The servlet container is starting.
* <p>
* Initialise the properties file using SystemProperties.initWebapp();
* and start Ebean.
* </p>
*/
public void contextInitialized(ServletContextEvent event) {
try {
ServletContext servletContext = event.getServletContext();
GlobalProperties.setServletContext(servletContext);
if (servletContext != null) {
String servletRealPath = servletContext.getRealPath("");
GlobalProperties.put("servlet.realpath", servletRealPath);
logger.info("servlet.realpath=[" + servletRealPath + "]");
}
Ebean.getServer(null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@@ -1,119 +1,100 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.List;
import java.util.Map;
import java.util.Set;
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<T> {
/**
* Return the query.
*/
public SpiQuery<T> getQuery();
/**
* Return the associated BeanDescriptor.
*/
public BeanDescriptor<?> getBeanDescriptor();
/**
* This will create a local (readOnly) transaction if no current transaction
* exists.
* <p>
* 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.
* </p>
*/
public void initTransIfRequired();
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
public void endTransIfRequired();
public void rollbackTransIfRequired();
/**
* Execute the query as findById.
*/
public Object findId();
/**
* Execute the find row count query.
*/
public int findRowCount();
/**
* Execute the find ids query.
*/
public List<Object> findIds();
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the find returning a QueryIterator.
*/
public QueryIterator<T> findIterate();
/**
* Execute the query as findList.
*/
public List<T> 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<T> getFromQueryCache();
package com.avaje.ebeaninternal.server.core;
import java.util.List;
import java.util.Map;
import java.util.Set;
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<T> {
/**
* Return the query.
*/
public SpiQuery<T> getQuery();
/**
* Return the associated BeanDescriptor.
*/
public BeanDescriptor<?> getBeanDescriptor();
/**
* This will create a local (readOnly) transaction if no current transaction
* exists.
* <p>
* 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.
* </p>
*/
public void initTransIfRequired();
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
public void endTransIfRequired();
public void rollbackTransIfRequired();
/**
* Execute the query as findById.
*/
public Object findId();
/**
* Execute the find row count query.
*/
public int findRowCount();
/**
* Execute the find ids query.
*/
public List<Object> findIds();
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the find returning a QueryIterator.
*/
public QueryIterator<T> findIterate();
/**
* Execute the query as findList.
*/
public List<T> 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<T> getFromQueryCache();
}
@@ -1,63 +1,44 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.util.concurrent.TimeUnit;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool;
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
/**
* BackgroundExecutor using my traditional ThreadPool that will grow and trim.
*
* @author rbygrave
*/
public class TraditionalBackgroundExecutor implements SpiBackgroundExecutor {
private final ThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*/
public TraditionalBackgroundExecutor(ThreadPool pool, int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.pool = pool;
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
}
/**
* Execute a Runnable using a background thread.
*/
public void execute(Runnable r) {
pool.assign(r, true);
}
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
}
public void shutdown() {
// the pool is shutdown automatically by the ThreadPoolManager
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.thread.ThreadPool;
/**
* BackgroundExecutor using my traditional ThreadPool that will grow and trim.
*
* @author rbygrave
*/
public class TraditionalBackgroundExecutor implements SpiBackgroundExecutor {
private final ThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
/**
* Construct the default implementation of BackgroundExecutor.
*/
public TraditionalBackgroundExecutor(ThreadPool pool, int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.pool = pool;
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
}
/**
* Execute a Runnable using a background thread.
*/
public void execute(Runnable r) {
pool.assign(r, true);
}
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
}
public void shutdown() {
// the pool is shutdown automatically by the ThreadPoolManager
schedulePool.shutdown();
}
}
@@ -1,83 +1,64 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.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<Dnode> ebeanOrmXml;
private final List<Dnode> ormXml;
private final List<Dnode> allXml;
public XmlConfig(List<Dnode> ormXml, List<Dnode> ebeanOrmXml){
this.ormXml = ormXml;
this.ebeanOrmXml = ebeanOrmXml;
this.allXml = new ArrayList<Dnode>(ormXml.size() + ebeanOrmXml.size());
allXml.addAll(ormXml);
allXml.addAll(ebeanOrmXml);
}
public List<Dnode> getEbeanOrmXml() {
return ebeanOrmXml;
}
public List<Dnode> getOrmXml() {
return ormXml;
}
public List<Dnode> find(List<Dnode> entityXml, String element) {
ArrayList<Dnode> hits = new ArrayList<Dnode>();
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.
* <p>
* This searches all the orm.xml and ebean-orm.xml files.
* </p>
*/
public List<Dnode> findEntityXml(String className) {
ArrayList<Dnode> hits = new ArrayList<Dnode>(2);
for (Dnode ormXml : allXml) {
Dnode entityMappings = ormXml.find("entity-mappings");
List<Dnode> 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<Dnode> ebeanOrmXml;
private final List<Dnode> ormXml;
private final List<Dnode> allXml;
public XmlConfig(List<Dnode> ormXml, List<Dnode> ebeanOrmXml){
this.ormXml = ormXml;
this.ebeanOrmXml = ebeanOrmXml;
this.allXml = new ArrayList<Dnode>(ormXml.size() + ebeanOrmXml.size());
allXml.addAll(ormXml);
allXml.addAll(ebeanOrmXml);
}
public List<Dnode> getEbeanOrmXml() {
return ebeanOrmXml;
}
public List<Dnode> getOrmXml() {
return ormXml;
}
public List<Dnode> find(List<Dnode> entityXml, String element) {
ArrayList<Dnode> hits = new ArrayList<Dnode>();
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.
* <p>
* This searches all the orm.xml and ebean-orm.xml files.
* </p>
*/
public List<Dnode> findEntityXml(String className) {
ArrayList<Dnode> hits = new ArrayList<Dnode>(2);
for (Dnode ormXml : allXml) {
Dnode entityMappings = ormXml.find("entity-mappings");
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
if (entities.size() == 1) {
hits.add(entities.get(0));
}
}
return hits;
}
}
@@ -1,192 +1,173 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
import com.avaje.ebeaninternal.server.util.ClassPathReader;
import com.avaje.ebeaninternal.server.util.DefaultClassPathReader;
/**
* Used to read the orm.xml and ebean-orm.xml configuration files.
*
* @author rbygrave
*/
public class XmlConfigLoader {
private static final Logger logger = Logger.getLogger(XmlConfigLoader.class.getName());
private final ClassPathReader classPathReader;
private final Object[] classPaths;
public XmlConfigLoader(ClassLoader classLoader){
if (classLoader == null) {
classLoader = getClass().getClassLoader();
}
String cn = GlobalProperties.get("ebean.classpathreader", null);
if (cn != null){
// use a user defined classPathReader
logger.info("Using ["+cn+"] to read the searchable class path");
this.classPathReader = (ClassPathReader)ClassUtil.newInstance(cn, this.getClass());
} else {
this.classPathReader = new DefaultClassPathReader();
}
this.classPaths = classPathReader.readPath(classLoader);
}
public XmlConfig load() {
List<Dnode> ormXml = search("META-INF/orm.xml");
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
return new XmlConfig(ormXml, ebeanOrmXml);
}
public List<Dnode> search(String searchFor) {
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
String charsetName = Charset.defaultCharset().name();
for (int h = 0; h < classPaths.length; h++) {
try {
// for each class path ...
File classPath;
if (URL.class.isInstance(classPaths[h])) {
classPath = new File(((URL) classPaths[h]).getFile());
} else {
classPath = new File(classPaths[h].toString());
}
// URL Decode the path replacing %20 to space characters.
String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName);
classPath = new File(path);
if (classPath.isDirectory()) {
checkDir(searchFor, xmlList, classPath);
} else if (classPath.getName().endsWith(".jar")) {
checkJar(searchFor, xmlList, classPath);
} else {
// this is not expected
String msg = "Not a Jar or Directory? " + classPath.getAbsolutePath();
logger.log(Level.SEVERE, msg);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return xmlList;
}
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
DnodeReader reader = new DnodeReader();
Dnode xmlDoc = reader.parseXml(is);
is.close();
xmlList.add(xmlDoc);
}
private void checkFile(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
File f = new File(dir, searchFor);
if (f.exists()){
FileInputStream fis = new FileInputStream(f);
BufferedInputStream is = new BufferedInputStream(fis);
processInputStream(xmlList, is);
}
}
private void checkDir(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
checkFile(searchFor, xmlList, dir);
if (dir.getPath().endsWith("classes")) {
// see if this is part of webapp and look for META-INF/searchFor
// relative to the WEB-INF/classes directory
File parent = dir.getParentFile();
if (parent != null && parent.getPath().endsWith("WEB-INF")){
parent = parent.getParentFile();
if (parent != null){
File metaInf = new File(parent, "META-INF");
if (metaInf.exists()){
checkFile(searchFor, xmlList, metaInf);
}
}
}
}
}
private void checkJar(String searchFor, ArrayList<Dnode> xmlList, File classPath) throws IOException {
String fileName = classPath.getName();
if (fileName.toLowerCase().startsWith("surefire")){
return;
}
JarFile module = null;
try {
module = new JarFile(classPath);
ZipEntry entry = module.getEntry(searchFor);
if (entry != null){
InputStream is = module.getInputStream(entry);
processInputStream(xmlList, is);
}
} catch (Exception e) {
logger.info("Unable to check jar file "+fileName+" for ebean-orm.xml");
} finally {
if (module != null){
module.close();
}
}
}
}
package com.avaje.ebeaninternal.server.core;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
import com.avaje.ebeaninternal.server.util.ClassPathReader;
import com.avaje.ebeaninternal.server.util.DefaultClassPathReader;
/**
* Used to read the orm.xml and ebean-orm.xml configuration files.
*
* @author rbygrave
*/
public class XmlConfigLoader {
private static final Logger logger = Logger.getLogger(XmlConfigLoader.class.getName());
private final ClassPathReader classPathReader;
private final Object[] classPaths;
public XmlConfigLoader(ClassLoader classLoader){
if (classLoader == null) {
classLoader = getClass().getClassLoader();
}
String cn = GlobalProperties.get("ebean.classpathreader", null);
if (cn != null){
// use a user defined classPathReader
logger.info("Using ["+cn+"] to read the searchable class path");
this.classPathReader = (ClassPathReader)ClassUtil.newInstance(cn, this.getClass());
} else {
this.classPathReader = new DefaultClassPathReader();
}
this.classPaths = classPathReader.readPath(classLoader);
}
public XmlConfig load() {
List<Dnode> ormXml = search("META-INF/orm.xml");
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
return new XmlConfig(ormXml, ebeanOrmXml);
}
public List<Dnode> search(String searchFor) {
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
String charsetName = Charset.defaultCharset().name();
for (int h = 0; h < classPaths.length; h++) {
try {
// for each class path ...
File classPath;
if (URL.class.isInstance(classPaths[h])) {
classPath = new File(((URL) classPaths[h]).getFile());
} else {
classPath = new File(classPaths[h].toString());
}
// URL Decode the path replacing %20 to space characters.
String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName);
classPath = new File(path);
if (classPath.isDirectory()) {
checkDir(searchFor, xmlList, classPath);
} else if (classPath.getName().endsWith(".jar")) {
checkJar(searchFor, xmlList, classPath);
} else {
// this is not expected
String msg = "Not a Jar or Directory? " + classPath.getAbsolutePath();
logger.log(Level.SEVERE, msg);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return xmlList;
}
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
DnodeReader reader = new DnodeReader();
Dnode xmlDoc = reader.parseXml(is);
is.close();
xmlList.add(xmlDoc);
}
private void checkFile(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
File f = new File(dir, searchFor);
if (f.exists()){
FileInputStream fis = new FileInputStream(f);
BufferedInputStream is = new BufferedInputStream(fis);
processInputStream(xmlList, is);
}
}
private void checkDir(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
checkFile(searchFor, xmlList, dir);
if (dir.getPath().endsWith("classes")) {
// see if this is part of webapp and look for META-INF/searchFor
// relative to the WEB-INF/classes directory
File parent = dir.getParentFile();
if (parent != null && parent.getPath().endsWith("WEB-INF")){
parent = parent.getParentFile();
if (parent != null){
File metaInf = new File(parent, "META-INF");
if (metaInf.exists()){
checkFile(searchFor, xmlList, metaInf);
}
}
}
}
}
private void checkJar(String searchFor, ArrayList<Dnode> xmlList, File classPath) throws IOException {
String fileName = classPath.getName();
if (fileName.toLowerCase().startsWith("surefire")){
return;
}
JarFile module = null;
try {
module = new JarFile(classPath);
ZipEntry entry = module.getEntry(searchFor);
if (entry != null){
InputStream is = module.getInputStream(entry);
processInputStream(xmlList, is);
}
} catch (Exception e) {
logger.info("Unable to check jar file "+fileName+" for ebean-orm.xml");
} finally {
if (module != null){
module.close();
}
}
}
}
@@ -1,65 +1,46 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.ddl;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor;
/**
* Base BeanVisitor that can help visiting inherited properties.
*
* @author rbygrave
*/
public abstract class AbstractBeanVisitor implements BeanVisitor {
/**
* Visit all the other inheritance properties that are not on the root.
*/
public void visitInheritanceProperties(BeanDescriptor<?> descriptor, PropertyVisitor pv) {
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()){
// add all properties on the children objects
InheritChildVisitor childVisitor = new InheritChildVisitor(pv);
inheritInfo.visitChildren(childVisitor);
}
}
/**
* Helper used to visit all the inheritInfo/BeanDescriptor in
* the inheritance hierarchy (to add their 'local' properties).
*/
protected static class InheritChildVisitor implements InheritInfoVisitor {
final PropertyVisitor pv;
protected InheritChildVisitor(PropertyVisitor pv) {
this.pv = pv;
}
public void visit(InheritInfo inheritInfo) {
BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal();
VisitorUtil.visit(propertiesLocal, pv);
}
}
}
package com.avaje.ebeaninternal.server.ddl;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor;
/**
* Base BeanVisitor that can help visiting inherited properties.
*
* @author rbygrave
*/
public abstract class AbstractBeanVisitor implements BeanVisitor {
/**
* Visit all the other inheritance properties that are not on the root.
*/
public void visitInheritanceProperties(BeanDescriptor<?> descriptor, PropertyVisitor pv) {
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()){
// add all properties on the children objects
InheritChildVisitor childVisitor = new InheritChildVisitor(pv);
inheritInfo.visitChildren(childVisitor);
}
}
/**
* Helper used to visit all the inheritInfo/BeanDescriptor in
* the inheritance hierarchy (to add their 'local' properties).
*/
protected static class InheritChildVisitor implements InheritInfoVisitor {
final PropertyVisitor pv;
protected InheritChildVisitor(PropertyVisitor pv) {
this.pv = pv;
}
public void visit(InheritInfo inheritInfo) {
BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal();
VisitorUtil.visit(propertiesLocal, pv);
}
}
}
@@ -1,259 +1,252 @@
/**
* Imilia Interactive Mobile Applications GmbH
* Copyright (c) 2009 - all rights reserved
*
* Created on: Jun 29, 2009
* Created by: emcgreal
*/
package com.avaje.ebeaninternal.server.ddl;
import java.io.StringWriter;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.type.ScalarType;
/**
* The context used during DDL generation.
*/
public class DdlGenContext {
private final StringWriter stringWriter = new StringWriter();
/**
* Used to map bean types to DB specific types.
*/
private final DbTypeMap dbTypeMap;
/**
* Handles DB specific DDL syntax.
*/
private final DbDdlSyntax ddlSyntax;
/**
* The new line character that is used.
*/
private final String newLine;
/**
* Last content written (used with removeLast())
*/
private final List<String> contentBuffer = new ArrayList<String>();
private Set<String> intersectionTables = new HashSet<String>();
private List<String> intersectionTablesCreateDdl = new ArrayList<String>();
private List<String> intersectionTablesFkDdl = new ArrayList<String>();
private final DatabasePlatform dbPlatform;
/** The Naming convention used to define FK an IX names */
private final NamingConvention namingConvention;
/** The global fk count used to keep FK names unique */
private int fkCount;
/** The ix count. */
private int ixCount;
public DdlGenContext(DatabasePlatform dbPlatform, NamingConvention namingConvention){
this.dbPlatform = dbPlatform;
this.dbTypeMap = dbPlatform.getDbTypeMap();
this.ddlSyntax = dbPlatform.getDbDdlSyntax();
this.newLine = ddlSyntax.getNewLine();
this.namingConvention = namingConvention;
}
/**
* Return the dbPlatform.
*/
public DatabasePlatform getDbPlatform() {
return dbPlatform;
}
public boolean isProcessIntersectionTable(String tableName){
return intersectionTables.add(tableName);
}
public void addCreateIntersectionTable(String createTableDdl){
intersectionTablesCreateDdl.add(createTableDdl);
}
public void addIntersectionTableFk(String intTableFk){
intersectionTablesFkDdl.add(intTableFk);
}
public void addIntersectionCreateTables() {
for (String intTableCreate : intersectionTablesCreateDdl) {
write(newLine);
write(intTableCreate);
}
}
public void addIntersectionFkeys() {
write(newLine);
write(newLine);
for (String intTableFk : intersectionTablesFkDdl) {
write(newLine);
write(intTableFk);
}
}
/**
* Return the generated content (DDL script).
*/
public String getContent(){
return stringWriter.toString();
}
/**
* Return the map used to determine the DB specific type
* for a given bean property.
*/
public DbTypeMap getDbTypeMap() {
return dbTypeMap;
}
/**
* Return object to handle DB specific DDL syntax.
*/
public DbDdlSyntax getDdlSyntax() {
return ddlSyntax;
}
public String getColumnDefn(BeanProperty p) {
DbType dbType = getDbType(p);
return p.renderDbType(dbType);
}
private DbType getDbType(BeanProperty p) {
ScalarType<?> scalarType = p.getScalarType();
if (scalarType == null) {
throw new RuntimeException("No scalarType for " + p.getFullBeanName());
}
if (p.isDbEncrypted()){
return dbTypeMap.get(p.getDbEncryptedType());
}
int jdbcType = scalarType.getJdbcType();
if (p.isLob() && jdbcType == Types.VARCHAR){
// workaround for Postgres TEXT type which is
// VARCHAR in jdbc API but TEXT in ddl
jdbcType = Types.CLOB;
}
return dbTypeMap.get(jdbcType);
}
/**
* Write content to the buffer.
*/
public DdlGenContext write(String content, int minWidth){
content = pad(content, minWidth);
contentBuffer.add(content);
return this;
}
/**
* Write content to the buffer.
*/
public DdlGenContext write(String content){
return write(content, 0);
}
public DdlGenContext writeNewLine() {
write(newLine);
return this;
}
/**
* Remove the last content that was written.
*/
public DdlGenContext removeLast() {
if (!contentBuffer.isEmpty()){
contentBuffer.remove(contentBuffer.size()-1);
} else {
throw new RuntimeException("No lastContent to remove?");
}
return this;
}
/**
* Flush the content to the buffer.
*/
public DdlGenContext flush() {
if (!contentBuffer.isEmpty()){
for (String s:contentBuffer){
if (s != null){
stringWriter.write(s);
}
}
contentBuffer.clear();
}
return this;
}
private String padding(int length){
StringBuffer sb = new StringBuffer(length);
for (int i = 0; i < length; i++) {
sb.append(" ");
}
return sb.toString();
}
public String pad(String content, int minWidth){
if (minWidth > 0 && content.length() < minWidth){
int padding = minWidth - content.length();
return content + padding(padding);
}
return content;
}
/**
* @return the namingConvention
*/
public NamingConvention getNamingConvention() {
return namingConvention;
}
/**
* @return the incremented fkCount
*/
public int incrementFkCount() {
return ++fkCount;
}
/**
* @return the incremented ixCount
*/
public int incrementIxCount() {
return ++ixCount;
}
/**
* Strips off the Database Platform specific quoted identifier characters.
*/
public String removeQuotes(String dbColumn) {
dbColumn = StringHelper.replaceString(dbColumn, dbPlatform.getOpenQuote(), "");
dbColumn = StringHelper.replaceString(dbColumn, dbPlatform.getCloseQuote(), "");
return dbColumn;
}
}
package com.avaje.ebeaninternal.server.ddl;
import java.io.StringWriter;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.type.ScalarType;
/**
* The context used during DDL generation.
*/
public class DdlGenContext {
private final StringWriter stringWriter = new StringWriter();
/**
* Used to map bean types to DB specific types.
*/
private final DbTypeMap dbTypeMap;
/**
* Handles DB specific DDL syntax.
*/
private final DbDdlSyntax ddlSyntax;
/**
* The new line character that is used.
*/
private final String newLine;
/**
* Last content written (used with removeLast())
*/
private final List<String> contentBuffer = new ArrayList<String>();
private Set<String> intersectionTables = new HashSet<String>();
private List<String> intersectionTablesCreateDdl = new ArrayList<String>();
private List<String> intersectionTablesFkDdl = new ArrayList<String>();
private final DatabasePlatform dbPlatform;
/** The Naming convention used to define FK an IX names */
private final NamingConvention namingConvention;
/** The global fk count used to keep FK names unique */
private int fkCount;
/** The ix count. */
private int ixCount;
public DdlGenContext(DatabasePlatform dbPlatform, NamingConvention namingConvention){
this.dbPlatform = dbPlatform;
this.dbTypeMap = dbPlatform.getDbTypeMap();
this.ddlSyntax = dbPlatform.getDbDdlSyntax();
this.newLine = ddlSyntax.getNewLine();
this.namingConvention = namingConvention;
}
/**
* Return the dbPlatform.
*/
public DatabasePlatform getDbPlatform() {
return dbPlatform;
}
public boolean isProcessIntersectionTable(String tableName){
return intersectionTables.add(tableName);
}
public void addCreateIntersectionTable(String createTableDdl){
intersectionTablesCreateDdl.add(createTableDdl);
}
public void addIntersectionTableFk(String intTableFk){
intersectionTablesFkDdl.add(intTableFk);
}
public void addIntersectionCreateTables() {
for (String intTableCreate : intersectionTablesCreateDdl) {
write(newLine);
write(intTableCreate);
}
}
public void addIntersectionFkeys() {
write(newLine);
write(newLine);
for (String intTableFk : intersectionTablesFkDdl) {
write(newLine);
write(intTableFk);
}
}
/**
* Return the generated content (DDL script).
*/
public String getContent(){
return stringWriter.toString();
}
/**
* Return the map used to determine the DB specific type
* for a given bean property.
*/
public DbTypeMap getDbTypeMap() {
return dbTypeMap;
}
/**
* Return object to handle DB specific DDL syntax.
*/
public DbDdlSyntax getDdlSyntax() {
return ddlSyntax;
}
public String getColumnDefn(BeanProperty p) {
DbType dbType = getDbType(p);
return p.renderDbType(dbType);
}
private DbType getDbType(BeanProperty p) {
ScalarType<?> scalarType = p.getScalarType();
if (scalarType == null) {
throw new RuntimeException("No scalarType for " + p.getFullBeanName());
}
if (p.isDbEncrypted()){
return dbTypeMap.get(p.getDbEncryptedType());
}
int jdbcType = scalarType.getJdbcType();
if (p.isLob() && jdbcType == Types.VARCHAR){
// workaround for Postgres TEXT type which is
// VARCHAR in jdbc API but TEXT in ddl
jdbcType = Types.CLOB;
}
return dbTypeMap.get(jdbcType);
}
/**
* Write content to the buffer.
*/
public DdlGenContext write(String content, int minWidth){
content = pad(content, minWidth);
contentBuffer.add(content);
return this;
}
/**
* Write content to the buffer.
*/
public DdlGenContext write(String content){
return write(content, 0);
}
public DdlGenContext writeNewLine() {
write(newLine);
return this;
}
/**
* Remove the last content that was written.
*/
public DdlGenContext removeLast() {
if (!contentBuffer.isEmpty()){
contentBuffer.remove(contentBuffer.size()-1);
} else {
throw new RuntimeException("No lastContent to remove?");
}
return this;
}
/**
* Flush the content to the buffer.
*/
public DdlGenContext flush() {
if (!contentBuffer.isEmpty()){
for (String s:contentBuffer){
if (s != null){
stringWriter.write(s);
}
}
contentBuffer.clear();
}
return this;
}
private String padding(int length){
StringBuffer sb = new StringBuffer(length);
for (int i = 0; i < length; i++) {
sb.append(" ");
}
return sb.toString();
}
public String pad(String content, int minWidth){
if (minWidth > 0 && content.length() < minWidth){
int padding = minWidth - content.length();
return content + padding(padding);
}
return content;
}
/**
* @return the namingConvention
*/
public NamingConvention getNamingConvention() {
return namingConvention;
}
/**
* @return the incremented fkCount
*/
public int incrementFkCount() {
return ++fkCount;
}
/**
* @return the incremented ixCount
*/
public int incrementIxCount() {
return ++ixCount;
}
/**
* Strips off the Database Platform specific quoted identifier characters.
*/
public String removeQuotes(String dbColumn) {
dbColumn = StringHelper.replaceString(dbColumn, dbPlatform.getOpenQuote(), "");
dbColumn = StringHelper.replaceString(dbColumn, dbPlatform.getCloseQuote(), "");
return dbColumn;
}
}
@@ -1,4 +1 @@
/**
* DDL generation.
*/
package com.avaje.ebeaninternal.server.ddl;
@@ -1,141 +1,122 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import javax.persistence.CascadeType;
/**
* Persist info for determining if save or delete should be performed.
* <p>
* This is set to associated Beans, Table joins and List.
* </p>
*/
public class BeanCascadeInfo {
/**
* should delete cascade.
*/
boolean delete;
/**
* Should save cascade.
*/
boolean save;
/**
* Should validate cascade.
*/
boolean validate;
/**
* Set the raw deployment attribute.
*/
public void setAttribute(String attr) {
if (attr == null){
return;
}
attr = attr.toLowerCase();
delete = (attr.indexOf("delete")>-1);
if (!delete){
// same as EJB3 remove
delete = (attr.indexOf("remove")>-1);
}
save = (attr.indexOf("save")>-1);
if (!save){
// same as EJB3 persist
save = (attr.indexOf("persist")>-1);
}
if (attr.indexOf("validate")>-1){
validate = true;
}
if (attr.indexOf("all")>-1){
delete = true;
save = true;
validate = true;
}
}
public void setTypes(CascadeType[] types) {
for (int i = 0; i < types.length; i++) {
setType(types[i]);
}
}
private void setType(CascadeType type) {
if (type.equals(CascadeType.ALL)){
save = true;
delete = true;
}
if (type.equals(CascadeType.REMOVE)){
delete = true;
}
if (type.equals(CascadeType.PERSIST)){
save = true;
}
if (type.equals(CascadeType.MERGE)){
save = true;
}
if (save || delete){
validate = true;
}
}
/**
* Return true if delete should cascade.
*/
public boolean isDelete() {
return delete;
}
/**
* Set to true if delete should cascade.
*/
public void setDelete(boolean isDelete) {
this.delete = isDelete;
}
/**
* Return true if save should cascade.
*/
public boolean isSave() {
return save;
}
/**
* Set to true if save should cascade.
*/
public void setSave(boolean isUpdate) {
this.save = isUpdate;
}
/**
* Return true if validate should be cascaded.
*/
public boolean isValidate() {
return validate;
}
/**
* Set validate to cascade or not.
*/
public void setValidate(boolean isValidate) {
this.validate = isValidate;
}
}
package com.avaje.ebeaninternal.server.deploy;
import javax.persistence.CascadeType;
/**
* Persist info for determining if save or delete should be performed.
* <p>
* This is set to associated Beans, Table joins and List.
* </p>
*/
public class BeanCascadeInfo {
/**
* should delete cascade.
*/
boolean delete;
/**
* Should save cascade.
*/
boolean save;
/**
* Should validate cascade.
*/
boolean validate;
/**
* Set the raw deployment attribute.
*/
public void setAttribute(String attr) {
if (attr == null){
return;
}
attr = attr.toLowerCase();
delete = (attr.indexOf("delete")>-1);
if (!delete){
// same as EJB3 remove
delete = (attr.indexOf("remove")>-1);
}
save = (attr.indexOf("save")>-1);
if (!save){
// same as EJB3 persist
save = (attr.indexOf("persist")>-1);
}
if (attr.indexOf("validate")>-1){
validate = true;
}
if (attr.indexOf("all")>-1){
delete = true;
save = true;
validate = true;
}
}
public void setTypes(CascadeType[] types) {
for (int i = 0; i < types.length; i++) {
setType(types[i]);
}
}
private void setType(CascadeType type) {
if (type.equals(CascadeType.ALL)){
save = true;
delete = true;
}
if (type.equals(CascadeType.REMOVE)){
delete = true;
}
if (type.equals(CascadeType.PERSIST)){
save = true;
}
if (type.equals(CascadeType.MERGE)){
save = true;
}
if (save || delete){
validate = true;
}
}
/**
* Return true if delete should cascade.
*/
public boolean isDelete() {
return delete;
}
/**
* Set to true if delete should cascade.
*/
public void setDelete(boolean isDelete) {
this.delete = isDelete;
}
/**
* Return true if save should cascade.
*/
public boolean isSave() {
return save;
}
/**
* Set to true if save should cascade.
*/
public void setSave(boolean isUpdate) {
this.save = isUpdate;
}
/**
* Return true if validate should be cascaded.
*/
public boolean isValidate() {
return validate;
}
/**
* Set validate to cascade or not.
*/
public void setValidate(boolean isValidate) {
this.validate = isValidate;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,56 +1,37 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
/**
* Provides a method to find a BeanDescriptor.
* <p>
* Used during deployment of to resolve relationships between beans.
* </p>
*/
public interface BeanDescriptorMap {
/**
* Return the name of the server/database.
*/
public String getServerName();
/**
* Return the Cache Manager.
*/
public ServerCacheManager getCacheManager();
/**
* Return the BeanDescriptor for a given class.
*/
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType);
/**
* Return the Encrypt key given the table and column name.
*/
public EncryptKey getEncryptKey(String tableName, String columnName);
public IdBinder createIdBinder(BeanProperty[] uids);
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
/**
* Provides a method to find a BeanDescriptor.
* <p>
* Used during deployment of to resolve relationships between beans.
* </p>
*/
public interface BeanDescriptorMap {
/**
* Return the name of the server/database.
*/
public String getServerName();
/**
* Return the Cache Manager.
*/
public ServerCacheManager getCacheManager();
/**
* Return the BeanDescriptor for a given class.
*/
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType);
/**
* Return the Encrypt key given the table and column name.
*/
public EncryptKey getEncryptKey(String tableName, String columnName);
public IdBinder createIdBinder(BeanProperty[] uids);
}
@@ -1,50 +1,31 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
public class BeanEmbeddedMeta {
final BeanProperty[] properties;
public BeanEmbeddedMeta(BeanProperty[] properties) {
this.properties = properties;
}
/**
* Return the properties with over ridden mapping information.
*/
public BeanProperty[] getProperties() {
return properties;
}
/**
* Return true if at least one property is a version property.
*/
public boolean isEmbeddedVersion() {
for (int i = 0; i < properties.length; i++) {
if (properties[i].isVersion()){
return true;
}
}
return false;
}
}
package com.avaje.ebeaninternal.server.deploy;
public class BeanEmbeddedMeta {
final BeanProperty[] properties;
public BeanEmbeddedMeta(BeanProperty[] properties) {
this.properties = properties;
}
/**
* Return the properties with over ridden mapping information.
*/
public BeanProperty[] getProperties() {
return properties;
}
/**
* Return true if at least one property is a version property.
*/
public boolean isEmbeddedVersion() {
for (int i = 0; i < properties.length; i++) {
if (properties[i].isVersion()){
return true;
}
}
return false;
}
}
@@ -1,72 +1,53 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.Map;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
/**
* Creates BeanProperties for Embedded beans that have deployment information
* such as the actual DB column name and table alias.
*/
public class BeanEmbeddedMetaFactory {
/**
* Create BeanProperties for embedded beans using the deployment specific DB column name and table alias.
*/
public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne<?> prop,
BeanDescriptor<?> descriptor) {
// we can get a BeanDescriptor for an Embedded bean
// and know that it is NOT recursive, as Embedded beans are
// only allow to hold simple scalar types...
BeanDescriptor<?> targetDesc = owner.getBeanDescriptor(prop.getTargetType());
if (targetDesc == null){
String msg = "Could not find BeanDescriptor for "+prop.getTargetType()
+". Perhaps the EmbeddedId class is not registered?";
throw new PersistenceException(msg);
}
// deployment override information (column names)
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
for (int i = 0; i < sourceProperties.length; i++) {
String propertyName = sourceProperties[i].getName();
String dbColumn = propColMap.get(propertyName);
if (dbColumn == null) {
// dbColumn not overridden so take original
dbColumn = sourceProperties[i].getDbColumn();
}
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.Map;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
/**
* Creates BeanProperties for Embedded beans that have deployment information
* such as the actual DB column name and table alias.
*/
public class BeanEmbeddedMetaFactory {
/**
* Create BeanProperties for embedded beans using the deployment specific DB column name and table alias.
*/
public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne<?> prop,
BeanDescriptor<?> descriptor) {
// we can get a BeanDescriptor for an Embedded bean
// and know that it is NOT recursive, as Embedded beans are
// only allow to hold simple scalar types...
BeanDescriptor<?> targetDesc = owner.getBeanDescriptor(prop.getTargetType());
if (targetDesc == null){
String msg = "Could not find BeanDescriptor for "+prop.getTargetType()
+". Perhaps the EmbeddedId class is not registered?";
throw new PersistenceException(msg);
}
// deployment override information (column names)
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
for (int i = 0; i < sourceProperties.length; i++) {
String propertyName = sourceProperties[i].getName();
String dbColumn = propColMap.get(propertyName);
if (dbColumn == null) {
// dbColumn not overridden so take original
dbColumn = sourceProperties[i].getDbColumn();
}
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
}
}
@@ -1,45 +1,26 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import com.avaje.ebean.event.BeanFinder;
/**
* Factory for controlling the construction of BeanFinders.
*/
public interface BeanFinderManager {
/**
* Return the number of beans with a registered finder.
*/
public int getRegisterCount();
/**
* Create the appropriate BeanController.
*/
public int createBeanFinders(List<Class<?>> finderClassList);
/**
* Return the BeanController for a given entity type.
*/
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType);
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import com.avaje.ebean.event.BeanFinder;
/**
* Factory for controlling the construction of BeanFinders.
*/
public interface BeanFinderManager {
/**
* Return the number of beans with a registered finder.
*/
public int getRegisterCount();
/**
* Create the appropriate BeanController.
*/
public int createBeanFinders(List<Class<?>> finderClassList);
/**
* Return the BeanController for a given entity type.
*/
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType);
}
@@ -1,75 +1,56 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
/**
* Represents a database foreign key which can map to an object relationship.
*/
public class BeanForeignKey {
private final String dbColumn;
private final int dbType;
/**
* Construct the BeanForeignKey.
*/
public BeanForeignKey(String dbColumn, int dbType) {
this.dbColumn = InternString.intern(dbColumn);
this.dbType = dbType;
}
/**
* Return the database column.
*/
public String getDbColumn() {
return dbColumn;
}
/**
* Return the JDBC datatype of the database column.
*/
public int getDbType() {
return dbType;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof BeanForeignKey) {
return obj.hashCode() == hashCode();
}
return false;
}
public int hashCode() {
int hc = getClass().hashCode();
hc = hc * 31 + (dbColumn != null ? dbColumn.hashCode() : 0);
return hc;
}
public String toString() {
return dbColumn;
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
/**
* Represents a database foreign key which can map to an object relationship.
*/
public class BeanForeignKey {
private final String dbColumn;
private final int dbType;
/**
* Construct the BeanForeignKey.
*/
public BeanForeignKey(String dbColumn, int dbType) {
this.dbColumn = InternString.intern(dbColumn);
this.dbType = dbType;
}
/**
* Return the database column.
*/
public String getDbColumn() {
return dbColumn;
}
/**
* Return the JDBC datatype of the database column.
*/
public int getDbType() {
return dbType;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof BeanForeignKey) {
return obj.hashCode() == hashCode();
}
return false;
}
public int hashCode() {
int hc = getClass().hashCode();
hc = hc * 31 + (dbColumn != null ? dbColumn.hashCode() : 0);
return hc;
}
public String toString() {
return dbColumn;
}
}
@@ -1,59 +1,40 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.persist.BeanPersister;
/**
* Holds the BeanDescriptor and its associated BeanPersister.
*/
public class BeanManager<T> {
private final BeanPersister persister;
private final BeanDescriptor<T> descriptor;
public BeanManager(BeanDescriptor<T> descriptor, BeanPersister persister) {
this.descriptor = descriptor;
this.persister = persister;
}
/**
* Return the associated BeanPersister.
*/
public BeanPersister getBeanPersister() {
return persister;
}
/**
* Return the BeanDescriptor.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return descriptor;
}
/**
* Return true if this bean type is an LDAP entity type.
*/
public boolean isLdapEntityType() {
return descriptor.isLdapEntityType();
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.persist.BeanPersister;
/**
* Holds the BeanDescriptor and its associated BeanPersister.
*/
public class BeanManager<T> {
private final BeanPersister persister;
private final BeanDescriptor<T> descriptor;
public BeanManager(BeanDescriptor<T> descriptor, BeanPersister persister) {
this.descriptor = descriptor;
this.persister = persister;
}
/**
* Return the associated BeanPersister.
*/
public BeanPersister getBeanPersister() {
return persister;
}
/**
* Return the BeanDescriptor.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return descriptor;
}
/**
* Return true if this bean type is an LDAP entity type.
*/
public boolean isLdapEntityType() {
return descriptor.isLdapEntityType();
}
}
@@ -1,46 +1,27 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.persist.BeanPersister;
import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory;
import com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersisterFactory;
/**
* Creates BeanManagers.
*/
public class BeanManagerFactory {
final BeanPersisterFactory peristerFactory;
public BeanManagerFactory(ServerConfig config, DatabasePlatform dbPlatform) {
peristerFactory = new DmlBeanPersisterFactory(dbPlatform);
}
public <T> BeanManager<T> create(BeanDescriptor<T> desc) {
BeanPersister persister = peristerFactory.create(desc);
return new BeanManager<T>(desc, persister);
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.persist.BeanPersister;
import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory;
import com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersisterFactory;
/**
* Creates BeanManagers.
*/
public class BeanManagerFactory {
final BeanPersisterFactory peristerFactory;
public BeanManagerFactory(ServerConfig config, DatabasePlatform dbPlatform) {
peristerFactory = new DmlBeanPersisterFactory(dbPlatform);
}
public <T> BeanManager<T> create(BeanDescriptor<T> desc) {
BeanPersister persister = peristerFactory.create(desc);
return new BeanManager<T>(desc, persister);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,402 +1,383 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdEmbedded;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdMultiple;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
*/
public abstract class BeanPropertyAssoc<T> extends BeanProperty {
private static final Logger logger = Logger.getLogger(BeanPropertyAssoc.class.getName());
/**
* The descriptor of the target. This MUST be initialised after construction
* so as to avoid a dependency loop between BeanDescriptors.
*/
BeanDescriptor<T> targetDescriptor;
IdBinder targetIdBinder;
InheritInfo targetInheritInfo;
String targetIdProperty;
/**
* Persist settings.
*/
final BeanCascadeInfo cascadeInfo;
/**
* Join between the beans.
*/
final TableJoin tableJoin;
/**
* The type of the joined bean.
*/
final Class<T> targetType;
/**
* The join table information.
*/
final BeanTable beanTable;
final String mappedBy;
/**
* Whether the associated join type should be an outer join.
*/
final boolean isOuterJoin;
String extraWhere;
boolean saveRecurseSkippable;
boolean deleteRecurseSkippable;
/**
* Construct the property.
*/
public BeanPropertyAssoc(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertyAssoc<T> deploy) {
super(owner, descriptor, deploy);
this.extraWhere = InternString.intern(deploy.getExtraWhere());
this.isOuterJoin = deploy.isOuterJoin();
this.beanTable = deploy.getBeanTable();
this.mappedBy = InternString.intern(deploy.getMappedBy());
this.tableJoin = new TableJoin(deploy.getTableJoin(), null);
this.targetType = deploy.getTargetType();
this.cascadeInfo = deploy.getCascadeInfo();
}
/**
* Initialise post construction.
*/
@Override
public void initialise() {
// this *MUST* execute after the BeanDescriptor is
// put into the map to stop infinite recursion
if (!isTransient){
targetDescriptor = descriptor.getBeanDescriptor(targetType);
targetIdBinder = targetDescriptor.getIdBinder();
targetInheritInfo = targetDescriptor.getInheritInfo();
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
deleteRecurseSkippable = targetDescriptor.isDeleteRecurseSkippable();
cascadeValidate = cascadeInfo.isValidate();
if (!targetIdBinder.isComplexId()){
targetIdProperty = targetIdBinder.getIdProperty();
}
}
}
/**
* Create a ElPropertyValue for a *ToOne or *ToMany.
*/
protected ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
// associated or embedded bean
BeanDescriptor<?> embDesc = getTargetDescriptor();
if (chain == null) {
chain = new ElPropertyChainBuilder(isEmbedded(), propName);
}
chain.add(this);
if (containsMany()) {
chain.setContainsMany(true);
}
return embDesc.buildElGetValue(remainder, chain, propertyDeploy);
}
/**
* Add table join with table alias based on prefix.
*/
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, prefix, ctx);
}
/**
* Add table join with explicit table alias.
*/
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, a1, a2, ctx);
}
/**
* Add table join with explicit table alias.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
tableJoin.addInnerJoin(a1, a2, ctx);
}
/**
* Return false.
*/
public boolean isScalar() {
return false;
}
/**
* Return the mappedBy property.
* This will be null on the owning side.
*/
public String getMappedBy() {
return mappedBy;
}
/**
* Return the Id property of the target entity type.
* <p>
* This will return null for multiple Id properties.
* </p>
*/
public String getTargetIdProperty() {
return targetIdProperty;
}
/**
* Return the BeanDescriptor of the target.
*/
public BeanDescriptor<T> getTargetDescriptor() {
return targetDescriptor;
}
public boolean isSaveRecurseSkippable(Object bean) {
if (!saveRecurseSkippable){
// we have to saveRecurse even if the bean is not dirty
// as this bean has cascade save on some of its properties
return false;
}
if (bean instanceof EntityBean){
return !((EntityBean)bean)._ebean_getIntercept().isNewOrDirty();
} else {
// we don't know so we say no
return false;
}
}
/**
* Return true if save can be skipped for unmodified bean(s) of this
* property.
* <p>
* That is, if a bean of this property is unmodified we don't need to
* saveRecurse because none of its associated beans have cascade save set to
* true.
* </p>
*/
public boolean isSaveRecurseSkippable() {
return saveRecurseSkippable;
}
/**
* Similar to isSaveRecurseSkippable but in terms of delete.
*/
public boolean isDeleteRecurseSkippable() {
return deleteRecurseSkippable;
}
/**
* Return true if the unique id properties are all not null for this bean.
*/
public boolean hasId(Object bean) {
BeanDescriptor<?> targetDesc = getTargetDescriptor();
BeanProperty[] uids = targetDesc.propertiesId();
for (int i = 0; i < uids.length; i++) {
Object value = uids[i].getValue(bean);
if (value == null) {
return false;
}
}
// all the unique properties are non-null
return true;
}
/**
* Return the type of the target.
* <p>
* This is the class of the associated bean, or beans contained in a list,
* set or map.
* </p>
*/
public Class<?> getTargetType() {
return targetType;
}
/**
* Return an extra clause to add to the query for loading or joining
* to this bean type.
*/
public String getExtraWhere() {
return extraWhere;
}
/**
* Return if this association should use an Outer join.
*/
public boolean isOuterJoin() {
return isOuterJoin;
}
/**
* Return true if this association is updateable.
*/
public boolean isUpdateable() {
if (tableJoin.columns().length > 0) {
return tableJoin.columns()[0].isUpdateable();
}
return true;
}
/**
* Return true if this association is insertable.
*/
public boolean isInsertable() {
if (tableJoin.columns().length > 0) {
return tableJoin.columns()[0].isInsertable();
}
return true;
}
/**
* return the join to use for the bean.
*/
public TableJoin getTableJoin() {
return tableJoin;
}
/**
* Return the BeanTable for this association.
* <p>
* This has the table name which is used to determine the relationship for
* this association.
* </p>
*/
public BeanTable getBeanTable() {
return beanTable;
}
/**
* Get the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Build the list of imported property. Matches BeanProperty from the target
* descriptor back to local database columns in the TableJoin.
*/
protected ImportedId createImportedId(BeanPropertyAssoc<?> owner, BeanDescriptor<?> target, TableJoin join) {
BeanProperty[] props = target.propertiesId();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isSqlSelectBased()){
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, props[0], 0);
}
TableJoinColumn[] cols = join.columns();
if (props.length == 1) {
if (!props[0].isEmbedded()) {
// simple single scalar id
if (cols.length != 1){
String msg = "No Imported Id column for ["+props[0]+"] in table ["+join.getTable()+"]";
logger.log(Level.SEVERE, msg);
return null;
} else {
return createImportedScalar(owner, cols[0], props, others);
}
} else {
// embedded id
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>)props[0];
BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar();
ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others);
return new ImportedIdEmbedded(owner, embProp, scalars);
}
} else {
// Concatenated key that is not embedded
ImportedIdSimple[] scalars = createImportedList(owner, cols, props, others);
return new ImportedIdMultiple(owner, scalars);
}
}
private ImportedIdSimple[] createImportedList(BeanPropertyAssoc<?> owner, TableJoinColumn[] cols, BeanProperty[] props, BeanProperty[] others) {
ArrayList<ImportedIdSimple> list = new ArrayList<ImportedIdSimple>();
for (int i = 0; i < cols.length; i++) {
list.add(createImportedScalar(owner, cols[i], props, others));
}
return ImportedIdSimple.sort(list);
}
private ImportedIdSimple createImportedScalar(BeanPropertyAssoc<?> owner, TableJoinColumn col, BeanProperty[] props, BeanProperty[] others) {
String matchColumn = col.getForeignDbColumn();
String localColumn = col.getLocalDbColumn();
for (int j = 0; j < props.length; j++) {
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, props[j], j);
}
}
for (int j = 0; j < others.length; j++) {
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, others[j], j+props.length);
}
}
String msg = "Error with the Join on ["+getFullBeanName()
+"]. Could not find the local match for ["+matchColumn+"] "//in table["+searchTable+"]?"
+" Perhaps an error in a @JoinColumn";
throw new PersistenceException(msg);
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdEmbedded;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdMultiple;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
*/
public abstract class BeanPropertyAssoc<T> extends BeanProperty {
private static final Logger logger = Logger.getLogger(BeanPropertyAssoc.class.getName());
/**
* The descriptor of the target. This MUST be initialised after construction
* so as to avoid a dependency loop between BeanDescriptors.
*/
BeanDescriptor<T> targetDescriptor;
IdBinder targetIdBinder;
InheritInfo targetInheritInfo;
String targetIdProperty;
/**
* Persist settings.
*/
final BeanCascadeInfo cascadeInfo;
/**
* Join between the beans.
*/
final TableJoin tableJoin;
/**
* The type of the joined bean.
*/
final Class<T> targetType;
/**
* The join table information.
*/
final BeanTable beanTable;
final String mappedBy;
/**
* Whether the associated join type should be an outer join.
*/
final boolean isOuterJoin;
String extraWhere;
boolean saveRecurseSkippable;
boolean deleteRecurseSkippable;
/**
* Construct the property.
*/
public BeanPropertyAssoc(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertyAssoc<T> deploy) {
super(owner, descriptor, deploy);
this.extraWhere = InternString.intern(deploy.getExtraWhere());
this.isOuterJoin = deploy.isOuterJoin();
this.beanTable = deploy.getBeanTable();
this.mappedBy = InternString.intern(deploy.getMappedBy());
this.tableJoin = new TableJoin(deploy.getTableJoin(), null);
this.targetType = deploy.getTargetType();
this.cascadeInfo = deploy.getCascadeInfo();
}
/**
* Initialise post construction.
*/
@Override
public void initialise() {
// this *MUST* execute after the BeanDescriptor is
// put into the map to stop infinite recursion
if (!isTransient){
targetDescriptor = descriptor.getBeanDescriptor(targetType);
targetIdBinder = targetDescriptor.getIdBinder();
targetInheritInfo = targetDescriptor.getInheritInfo();
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
deleteRecurseSkippable = targetDescriptor.isDeleteRecurseSkippable();
cascadeValidate = cascadeInfo.isValidate();
if (!targetIdBinder.isComplexId()){
targetIdProperty = targetIdBinder.getIdProperty();
}
}
}
/**
* Create a ElPropertyValue for a *ToOne or *ToMany.
*/
protected ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
// associated or embedded bean
BeanDescriptor<?> embDesc = getTargetDescriptor();
if (chain == null) {
chain = new ElPropertyChainBuilder(isEmbedded(), propName);
}
chain.add(this);
if (containsMany()) {
chain.setContainsMany(true);
}
return embDesc.buildElGetValue(remainder, chain, propertyDeploy);
}
/**
* Add table join with table alias based on prefix.
*/
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, prefix, ctx);
}
/**
* Add table join with explicit table alias.
*/
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, a1, a2, ctx);
}
/**
* Add table join with explicit table alias.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
tableJoin.addInnerJoin(a1, a2, ctx);
}
/**
* Return false.
*/
public boolean isScalar() {
return false;
}
/**
* Return the mappedBy property.
* This will be null on the owning side.
*/
public String getMappedBy() {
return mappedBy;
}
/**
* Return the Id property of the target entity type.
* <p>
* This will return null for multiple Id properties.
* </p>
*/
public String getTargetIdProperty() {
return targetIdProperty;
}
/**
* Return the BeanDescriptor of the target.
*/
public BeanDescriptor<T> getTargetDescriptor() {
return targetDescriptor;
}
public boolean isSaveRecurseSkippable(Object bean) {
if (!saveRecurseSkippable){
// we have to saveRecurse even if the bean is not dirty
// as this bean has cascade save on some of its properties
return false;
}
if (bean instanceof EntityBean){
return !((EntityBean)bean)._ebean_getIntercept().isNewOrDirty();
} else {
// we don't know so we say no
return false;
}
}
/**
* Return true if save can be skipped for unmodified bean(s) of this
* property.
* <p>
* That is, if a bean of this property is unmodified we don't need to
* saveRecurse because none of its associated beans have cascade save set to
* true.
* </p>
*/
public boolean isSaveRecurseSkippable() {
return saveRecurseSkippable;
}
/**
* Similar to isSaveRecurseSkippable but in terms of delete.
*/
public boolean isDeleteRecurseSkippable() {
return deleteRecurseSkippable;
}
/**
* Return true if the unique id properties are all not null for this bean.
*/
public boolean hasId(Object bean) {
BeanDescriptor<?> targetDesc = getTargetDescriptor();
BeanProperty[] uids = targetDesc.propertiesId();
for (int i = 0; i < uids.length; i++) {
Object value = uids[i].getValue(bean);
if (value == null) {
return false;
}
}
// all the unique properties are non-null
return true;
}
/**
* Return the type of the target.
* <p>
* This is the class of the associated bean, or beans contained in a list,
* set or map.
* </p>
*/
public Class<?> getTargetType() {
return targetType;
}
/**
* Return an extra clause to add to the query for loading or joining
* to this bean type.
*/
public String getExtraWhere() {
return extraWhere;
}
/**
* Return if this association should use an Outer join.
*/
public boolean isOuterJoin() {
return isOuterJoin;
}
/**
* Return true if this association is updateable.
*/
public boolean isUpdateable() {
if (tableJoin.columns().length > 0) {
return tableJoin.columns()[0].isUpdateable();
}
return true;
}
/**
* Return true if this association is insertable.
*/
public boolean isInsertable() {
if (tableJoin.columns().length > 0) {
return tableJoin.columns()[0].isInsertable();
}
return true;
}
/**
* return the join to use for the bean.
*/
public TableJoin getTableJoin() {
return tableJoin;
}
/**
* Return the BeanTable for this association.
* <p>
* This has the table name which is used to determine the relationship for
* this association.
* </p>
*/
public BeanTable getBeanTable() {
return beanTable;
}
/**
* Get the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Build the list of imported property. Matches BeanProperty from the target
* descriptor back to local database columns in the TableJoin.
*/
protected ImportedId createImportedId(BeanPropertyAssoc<?> owner, BeanDescriptor<?> target, TableJoin join) {
BeanProperty[] props = target.propertiesId();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isSqlSelectBased()){
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, props[0], 0);
}
TableJoinColumn[] cols = join.columns();
if (props.length == 1) {
if (!props[0].isEmbedded()) {
// simple single scalar id
if (cols.length != 1){
String msg = "No Imported Id column for ["+props[0]+"] in table ["+join.getTable()+"]";
logger.log(Level.SEVERE, msg);
return null;
} else {
return createImportedScalar(owner, cols[0], props, others);
}
} else {
// embedded id
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>)props[0];
BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar();
ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others);
return new ImportedIdEmbedded(owner, embProp, scalars);
}
} else {
// Concatenated key that is not embedded
ImportedIdSimple[] scalars = createImportedList(owner, cols, props, others);
return new ImportedIdMultiple(owner, scalars);
}
}
private ImportedIdSimple[] createImportedList(BeanPropertyAssoc<?> owner, TableJoinColumn[] cols, BeanProperty[] props, BeanProperty[] others) {
ArrayList<ImportedIdSimple> list = new ArrayList<ImportedIdSimple>();
for (int i = 0; i < cols.length; i++) {
list.add(createImportedScalar(owner, cols[i], props, others));
}
return ImportedIdSimple.sort(list);
}
private ImportedIdSimple createImportedScalar(BeanPropertyAssoc<?> owner, TableJoinColumn col, BeanProperty[] props, BeanProperty[] others) {
String matchColumn = col.getForeignDbColumn();
String localColumn = col.getLocalDbColumn();
for (int j = 0; j < props.length; j++) {
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, props[j], j);
}
}
for (int j = 0; j < others.length; j++) {
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, others[j], j+props.length);
}
}
String msg = "Error with the Join on ["+getFullBeanName()
+"]. Could not find the local match for ["+matchColumn+"] "//in table["+searchTable+"]?"
+" Perhaps an error in a @JoinColumn";
throw new PersistenceException(msg);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,229 +1,210 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Property mapped to an Immutable Compound Value Object.
* <p>
* An Immutable Compound Value Object is similar to an Embedded bean but it
* doesn't require enhancement and MUST be treated as an Immutable type.
* </p>
*/
public class BeanPropertyCompound extends BeanProperty {
private final CtCompoundType<?> compoundType;
/**
* Type Converter for scala.Option and similar type wrapping.
*/
@SuppressWarnings("rawtypes")
private final ScalarTypeConverter typeConverter;
private final BeanProperty[] scalarProperties;
private final LinkedHashMap<String, BeanProperty> propertyMap = new LinkedHashMap<String, BeanProperty>();
private final LinkedHashMap<String, CtCompoundPropertyElAdapter> nonScalarMap = new LinkedHashMap<String, CtCompoundPropertyElAdapter>();
private final BeanPropertyCompoundRoot root;
/**
* Create the property.
*/
public BeanPropertyCompound(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertyCompound deploy) {
super(owner, descriptor, deploy);
this.compoundType = deploy.getCompoundType();
this.typeConverter = deploy.getTypeConverter();
this.root = deploy.getFlatProperties(owner, descriptor);
this.scalarProperties = root.getScalarProperties();
for (int i = 0; i < scalarProperties.length; i++) {
propertyMap.put(scalarProperties[i].getName(), scalarProperties[i]);
}
List<CtCompoundProperty> nonScalarPropsList = root.getNonScalarProperties();
for (int i = 0; i < nonScalarPropsList.size(); i++) {
CtCompoundProperty ctProp = nonScalarPropsList.get(i);
CtCompoundPropertyElAdapter adapter = new CtCompoundPropertyElAdapter(ctProp);
nonScalarMap.put(ctProp.getRelativeName(), adapter);
}
}
@Override
public void initialise() {
// do nothing for normal BeanProperty
if (!isTransient && compoundType == null) {
String msg = "No cvoInternalType assigned to " + descriptor.getFullName() + "." + getName();
throw new RuntimeException(msg);
}
}
@Override
public void setDeployOrder(int deployOrder) {
this.deployOrder = deployOrder;
for (CtCompoundPropertyElAdapter adapter : nonScalarMap.values()) {
adapter.setDeployOrder(deployOrder);
}
}
/**
* Get the underlying compound type.
*/
@SuppressWarnings("unchecked")
public Object getValueUnderlying(Object bean) {
Object value = getValue(bean);
if (typeConverter != null){
value = typeConverter.unwrapValue(value);
}
return value;
}
@Override
public Object getValue(Object bean) {
return super.getValue(bean);
}
@Override
public Object getValueIntercept(Object bean) {
return super.getValueIntercept(bean);
}
@Override
public void setValue(Object bean, Object value) {
super.setValue(bean, value);
}
@Override
public void setValueIntercept(Object bean, Object value) {
super.setValueIntercept(bean, value);
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
if (chain == null) {
chain = new ElPropertyChainBuilder(true, propName);
}
// first add this property
chain.add(this);
// handle all the rest of the chain handled by the
// BeanProperty (all depth for nested compound type)
BeanProperty p = propertyMap.get(remainder);
if (p != null) {
return chain.add(p).build();
}
CtCompoundPropertyElAdapter elAdapter = nonScalarMap.get(remainder);
if (elAdapter == null) {
throw new RuntimeException("property [" + remainder + "] not found in " + getFullBeanName());
}
return chain.add(elAdapter).build();
}
@Override
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!isTransient) {
for (int i = 0; i < scalarProperties.length; i++) {
scalarProperties[i].appendSelect(ctx, subQuery);
}
}
}
public BeanProperty[] getScalarProperties() {
return scalarProperties;
}
@Override
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
boolean assignable = (type == null || owningType.isAssignableFrom(type));
Object v = compoundType.read(ctx.getDataReader());
if (assignable) {
setValue(bean, v);
}
return v;
}
/**
* Read the data from the resultSet effectively ignoring it and returning
* null.
*/
@SuppressWarnings("unchecked")
@Override
public Object read(DbReadContext ctx) throws SQLException {
Object v = compoundType.read(ctx.getDataReader());
if (typeConverter != null){
v = typeConverter.wrapValue(v);
}
return v;
}
@Override
public void loadIgnore(DbReadContext ctx) {
compoundType.loadIgnore(ctx.getDataReader());
}
@Override
public void load(SqlBeanLoad sqlBeanLoad) throws SQLException {
sqlBeanLoad.load(this);
}
@Override
public Object elGetReference(Object bean) {
return bean;
}
public void jsonWrite(WriteJsonContext ctx, Object bean) {
Object valueObject = getValueIntercept(bean);
compoundType.jsonWrite(ctx, valueObject, name);
}
public void jsonRead(ReadJsonContext ctx, Object bean){
Object objValue = compoundType.jsonRead(ctx);
setValue(bean, objValue);
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Property mapped to an Immutable Compound Value Object.
* <p>
* An Immutable Compound Value Object is similar to an Embedded bean but it
* doesn't require enhancement and MUST be treated as an Immutable type.
* </p>
*/
public class BeanPropertyCompound extends BeanProperty {
private final CtCompoundType<?> compoundType;
/**
* Type Converter for scala.Option and similar type wrapping.
*/
@SuppressWarnings("rawtypes")
private final ScalarTypeConverter typeConverter;
private final BeanProperty[] scalarProperties;
private final LinkedHashMap<String, BeanProperty> propertyMap = new LinkedHashMap<String, BeanProperty>();
private final LinkedHashMap<String, CtCompoundPropertyElAdapter> nonScalarMap = new LinkedHashMap<String, CtCompoundPropertyElAdapter>();
private final BeanPropertyCompoundRoot root;
/**
* Create the property.
*/
public BeanPropertyCompound(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertyCompound deploy) {
super(owner, descriptor, deploy);
this.compoundType = deploy.getCompoundType();
this.typeConverter = deploy.getTypeConverter();
this.root = deploy.getFlatProperties(owner, descriptor);
this.scalarProperties = root.getScalarProperties();
for (int i = 0; i < scalarProperties.length; i++) {
propertyMap.put(scalarProperties[i].getName(), scalarProperties[i]);
}
List<CtCompoundProperty> nonScalarPropsList = root.getNonScalarProperties();
for (int i = 0; i < nonScalarPropsList.size(); i++) {
CtCompoundProperty ctProp = nonScalarPropsList.get(i);
CtCompoundPropertyElAdapter adapter = new CtCompoundPropertyElAdapter(ctProp);
nonScalarMap.put(ctProp.getRelativeName(), adapter);
}
}
@Override
public void initialise() {
// do nothing for normal BeanProperty
if (!isTransient && compoundType == null) {
String msg = "No cvoInternalType assigned to " + descriptor.getFullName() + "." + getName();
throw new RuntimeException(msg);
}
}
@Override
public void setDeployOrder(int deployOrder) {
this.deployOrder = deployOrder;
for (CtCompoundPropertyElAdapter adapter : nonScalarMap.values()) {
adapter.setDeployOrder(deployOrder);
}
}
/**
* Get the underlying compound type.
*/
@SuppressWarnings("unchecked")
public Object getValueUnderlying(Object bean) {
Object value = getValue(bean);
if (typeConverter != null){
value = typeConverter.unwrapValue(value);
}
return value;
}
@Override
public Object getValue(Object bean) {
return super.getValue(bean);
}
@Override
public Object getValueIntercept(Object bean) {
return super.getValueIntercept(bean);
}
@Override
public void setValue(Object bean, Object value) {
super.setValue(bean, value);
}
@Override
public void setValueIntercept(Object bean, Object value) {
super.setValueIntercept(bean, value);
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
if (chain == null) {
chain = new ElPropertyChainBuilder(true, propName);
}
// first add this property
chain.add(this);
// handle all the rest of the chain handled by the
// BeanProperty (all depth for nested compound type)
BeanProperty p = propertyMap.get(remainder);
if (p != null) {
return chain.add(p).build();
}
CtCompoundPropertyElAdapter elAdapter = nonScalarMap.get(remainder);
if (elAdapter == null) {
throw new RuntimeException("property [" + remainder + "] not found in " + getFullBeanName());
}
return chain.add(elAdapter).build();
}
@Override
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!isTransient) {
for (int i = 0; i < scalarProperties.length; i++) {
scalarProperties[i].appendSelect(ctx, subQuery);
}
}
}
public BeanProperty[] getScalarProperties() {
return scalarProperties;
}
@Override
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
boolean assignable = (type == null || owningType.isAssignableFrom(type));
Object v = compoundType.read(ctx.getDataReader());
if (assignable) {
setValue(bean, v);
}
return v;
}
/**
* Read the data from the resultSet effectively ignoring it and returning
* null.
*/
@SuppressWarnings("unchecked")
@Override
public Object read(DbReadContext ctx) throws SQLException {
Object v = compoundType.read(ctx.getDataReader());
if (typeConverter != null){
v = typeConverter.wrapValue(v);
}
return v;
}
@Override
public void loadIgnore(DbReadContext ctx) {
compoundType.loadIgnore(ctx.getDataReader());
}
@Override
public void load(SqlBeanLoad sqlBeanLoad) throws SQLException {
sqlBeanLoad.load(this);
}
@Override
public Object elGetReference(Object bean) {
return bean;
}
public void jsonWrite(WriteJsonContext ctx, Object bean) {
Object valueObject = getValueIntercept(bean);
compoundType.jsonWrite(ctx, valueObject, name);
}
public void jsonRead(ReadJsonContext ctx, Object bean){
Object objValue = compoundType.jsonRead(ctx);
setValue(bean, objValue);
}
}
@@ -1,129 +1,110 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
/**
* Represents the root BeanProperty for properties of a compound type.
* <p>
* Holds all the scalar and non-scalar properties of the compound type. The
* scalar properties match to DB columns and the non-scalar ones are here solely
* to support EL expression language for nested compound types.
* </p>
*
* @author rbygrave
*/
public class BeanPropertyCompoundRoot {
private final BeanReflectSetter setter;
/**
* The method used to write the property.
*/
private final Method writeMethod;
private final String name;
private final String fullBeanName;
private final LinkedHashMap<String, BeanPropertyCompoundScalar> propMap;
private final ArrayList<BeanPropertyCompoundScalar> propList;
private List<CtCompoundProperty> nonScalarProperties;
public BeanPropertyCompoundRoot(DeployBeanProperty deploy) {
this.fullBeanName = deploy.getFullBeanName();
this.name = deploy.getName();
this.setter = deploy.getSetter();
this.writeMethod = deploy.getWriteMethod();
this.propList = new ArrayList<BeanPropertyCompoundScalar>();
this.propMap = new LinkedHashMap<String, BeanPropertyCompoundScalar>();
}
public BeanProperty[] getScalarProperties() {
return propList.toArray(new BeanProperty[propList.size()]);
}
public void register(BeanPropertyCompoundScalar prop) {
propList.add(prop);
propMap.put(prop.getName(), prop);
}
public BeanPropertyCompoundScalar getCompoundScalarProperty(String propName) {
return propMap.get(propName);
}
public List<CtCompoundProperty> getNonScalarProperties() {
return nonScalarProperties;
}
public void setNonScalarProperties(List<CtCompoundProperty> nonScalarProperties) {
this.nonScalarProperties = nonScalarProperties;
}
/**
* Set the value of the property without interception or
* PropertyChangeSupport.
*/
public void setRootValue(Object bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.set(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "set " + name + " with arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error";
throw new RuntimeException(msg, ex);
}
}
/**
* Set the value of the property.
*/
public void setRootValueIntercept(Object bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.setIntercept(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "setIntercept " + name + " arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error";
throw new RuntimeException(msg, ex);
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
/**
* Represents the root BeanProperty for properties of a compound type.
* <p>
* Holds all the scalar and non-scalar properties of the compound type. The
* scalar properties match to DB columns and the non-scalar ones are here solely
* to support EL expression language for nested compound types.
* </p>
*
* @author rbygrave
*/
public class BeanPropertyCompoundRoot {
private final BeanReflectSetter setter;
/**
* The method used to write the property.
*/
private final Method writeMethod;
private final String name;
private final String fullBeanName;
private final LinkedHashMap<String, BeanPropertyCompoundScalar> propMap;
private final ArrayList<BeanPropertyCompoundScalar> propList;
private List<CtCompoundProperty> nonScalarProperties;
public BeanPropertyCompoundRoot(DeployBeanProperty deploy) {
this.fullBeanName = deploy.getFullBeanName();
this.name = deploy.getName();
this.setter = deploy.getSetter();
this.writeMethod = deploy.getWriteMethod();
this.propList = new ArrayList<BeanPropertyCompoundScalar>();
this.propMap = new LinkedHashMap<String, BeanPropertyCompoundScalar>();
}
public BeanProperty[] getScalarProperties() {
return propList.toArray(new BeanProperty[propList.size()]);
}
public void register(BeanPropertyCompoundScalar prop) {
propList.add(prop);
propMap.put(prop.getName(), prop);
}
public BeanPropertyCompoundScalar getCompoundScalarProperty(String propName) {
return propMap.get(propName);
}
public List<CtCompoundProperty> getNonScalarProperties() {
return nonScalarProperties;
}
public void setNonScalarProperties(List<CtCompoundProperty> nonScalarProperties) {
this.nonScalarProperties = nonScalarProperties;
}
/**
* Set the value of the property without interception or
* PropertyChangeSupport.
*/
public void setRootValue(Object bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.set(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "set " + name + " with arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error";
throw new RuntimeException(msg, ex);
}
}
/**
* Set the value of the property.
*/
public void setRootValueIntercept(Object bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.setIntercept(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "setIntercept " + name + " arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error";
throw new RuntimeException(msg, ex);
}
}
}
@@ -1,120 +1,101 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
/**
* A BeanProperty owned by a Compound value object that maps to
* a real scalar type.
*
* @author rbygrave
*/
public class BeanPropertyCompoundScalar extends BeanProperty {
private final BeanPropertyCompoundRoot rootProperty;
private final CtCompoundProperty ctProperty;
@SuppressWarnings("rawtypes")
private final ScalarTypeConverter typeConverter;
public BeanPropertyCompoundScalar(BeanPropertyCompoundRoot rootProperty, DeployBeanProperty scalarDeploy,
CtCompoundProperty ctProperty, ScalarTypeConverter<?, ?> typeConverter) {
super(scalarDeploy);
this.rootProperty = rootProperty;
this.ctProperty = ctProperty;
this.typeConverter = typeConverter;
}
@SuppressWarnings("unchecked")
@Override
public Object getValue(Object valueObject) {
if (typeConverter != null){
valueObject = typeConverter.unwrapValue(valueObject);
}
return ctProperty.getValue(valueObject);
}
@Override
public void setValue(Object bean, Object value) {
setValueInCompound(bean, value, false);
}
@SuppressWarnings("unchecked")
public void setValueInCompound(Object bean, Object value, boolean intercept) {
Object compoundValue = ctProperty.setValue(bean, value);
if (compoundValue != null){
if (typeConverter != null){
compoundValue = typeConverter.wrapValue(compoundValue);
}
// we are at the top level and we have a compound value
// that we can set using the root property
if (intercept){
rootProperty.setRootValueIntercept(bean, compoundValue);
} else {
rootProperty.setRootValue(bean, compoundValue);
}
}
}
/**
* No interception on embedded scalar values inside a CVO.
*/
@Override
public void setValueIntercept(Object bean, Object value) {
setValueInCompound(bean, value, true);
}
/**
* No interception on embedded scalar values inside a CVO.
*/
@Override
public Object getValueIntercept(Object bean) {
return getValue(bean);
}
@Override
public Object elGetReference(Object bean) {
return getValue(bean);
}
@Override
public Object elGetValue(Object bean) {
return getValue(bean);
}
@Override
public void elSetReference(Object bean) {
super.elSetReference(bean);
}
@Override
public void elSetValue(Object bean, Object value, boolean populate, boolean reference) {
super.elSetValue(bean, value, populate, reference);
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
/**
* A BeanProperty owned by a Compound value object that maps to
* a real scalar type.
*
* @author rbygrave
*/
public class BeanPropertyCompoundScalar extends BeanProperty {
private final BeanPropertyCompoundRoot rootProperty;
private final CtCompoundProperty ctProperty;
@SuppressWarnings("rawtypes")
private final ScalarTypeConverter typeConverter;
public BeanPropertyCompoundScalar(BeanPropertyCompoundRoot rootProperty, DeployBeanProperty scalarDeploy,
CtCompoundProperty ctProperty, ScalarTypeConverter<?, ?> typeConverter) {
super(scalarDeploy);
this.rootProperty = rootProperty;
this.ctProperty = ctProperty;
this.typeConverter = typeConverter;
}
@SuppressWarnings("unchecked")
@Override
public Object getValue(Object valueObject) {
if (typeConverter != null){
valueObject = typeConverter.unwrapValue(valueObject);
}
return ctProperty.getValue(valueObject);
}
@Override
public void setValue(Object bean, Object value) {
setValueInCompound(bean, value, false);
}
@SuppressWarnings("unchecked")
public void setValueInCompound(Object bean, Object value, boolean intercept) {
Object compoundValue = ctProperty.setValue(bean, value);
if (compoundValue != null){
if (typeConverter != null){
compoundValue = typeConverter.wrapValue(compoundValue);
}
// we are at the top level and we have a compound value
// that we can set using the root property
if (intercept){
rootProperty.setRootValueIntercept(bean, compoundValue);
} else {
rootProperty.setRootValue(bean, compoundValue);
}
}
}
/**
* No interception on embedded scalar values inside a CVO.
*/
@Override
public void setValueIntercept(Object bean, Object value) {
setValueInCompound(bean, value, true);
}
/**
* No interception on embedded scalar values inside a CVO.
*/
@Override
public Object getValueIntercept(Object bean) {
return getValue(bean);
}
@Override
public Object elGetReference(Object bean) {
return getValue(bean);
}
@Override
public Object elGetValue(Object bean) {
return getValue(bean);
}
@Override
public void elSetReference(Object bean) {
super.elSetReference(bean);
}
@Override
public void elSetValue(Object bean, Object value, boolean populate, boolean reference) {
super.elSetValue(bean, value, populate, reference);
}
}
@@ -1,64 +1,45 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
/**
* Used hold meta data when a bean property is overridden.
* <p>
* Typically this is for Embedded Beans.
* </p>
*/
public class BeanPropertyOverride {
private final String dbColumn;
private final String sqlFormulaSelect;
private final String sqlFormulaJoin;
public BeanPropertyOverride(String dbColumn) {
this(dbColumn, null, null);
}
public BeanPropertyOverride(String dbColumn, String sqlFormulaSelect, String sqlFormulaJoin) {
this.dbColumn = InternString.intern(dbColumn);
this.sqlFormulaSelect = InternString.intern(sqlFormulaSelect);
this.sqlFormulaJoin = InternString.intern(sqlFormulaJoin);
}
public String getDbColumn() {
return dbColumn;
}
public String getSqlFormulaSelect() {
return sqlFormulaSelect;
}
public String getSqlFormulaJoin() {
return sqlFormulaJoin;
}
public String replace(String src, String srcDbColumn){
return StringHelper.replaceString(src, srcDbColumn, dbColumn);
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
/**
* Used hold meta data when a bean property is overridden.
* <p>
* Typically this is for Embedded Beans.
* </p>
*/
public class BeanPropertyOverride {
private final String dbColumn;
private final String sqlFormulaSelect;
private final String sqlFormulaJoin;
public BeanPropertyOverride(String dbColumn) {
this(dbColumn, null, null);
}
public BeanPropertyOverride(String dbColumn, String sqlFormulaSelect, String sqlFormulaJoin) {
this.dbColumn = InternString.intern(dbColumn);
this.sqlFormulaSelect = InternString.intern(sqlFormulaSelect);
this.sqlFormulaJoin = InternString.intern(sqlFormulaJoin);
}
public String getDbColumn() {
return dbColumn;
}
public String getSqlFormulaSelect() {
return sqlFormulaSelect;
}
public String getSqlFormulaJoin() {
return sqlFormulaJoin;
}
public String replace(String src, String srcDbColumn){
return StringHelper.replaceString(src, srcDbColumn, dbColumn);
}
}
@@ -1,99 +1,80 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.Iterator;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException;
import com.avaje.ebeaninternal.server.type.ScalarType;
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
private final ScalarType<T> collectionScalarType;
public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
super(owner, descriptor, deploy);
this.collectionScalarType = deploy.getCollectionScalarType();
}
public void initialise() {
super.initialise();
}
@Override
public Attribute createAttribute(Object bean) {
Object v = getValue(bean);
if (v == null){
return null;
}
if (ldapAttributeAdapter != null){
return ldapAttributeAdapter.createAttribute(v);
}
BasicAttribute attrs = new BasicAttribute(getDbColumn());
Iterator<?> it = help.getIterator(v);
if (it != null){
while (it.hasNext()) {
Object beanValue = it.next();
Object attrValue = collectionScalarType.toJdbcType(beanValue);
attrs.add(attrValue);
}
}
return attrs;
}
@Override
public void setAttributeValue(Object bean, Attribute attr) {
try {
if (attr != null){
Object beanValue;
if (ldapAttributeAdapter != null){
beanValue = ldapAttributeAdapter.readAttribute(attr);
} else {
boolean vanilla = true;
beanValue = help.createEmpty(vanilla);
BeanCollectionAdd collAdd = help.getBeanCollectionAdd(beanValue, mapKey);
NamingEnumeration<?> en = attr.getAll();
while (en.hasMoreElements()) {
Object attrValue = (Object) en.nextElement();
Object collValue = collectionScalarType.toBeanType(attrValue);
collAdd.addBean(collValue);
}
}
setValue(bean, beanValue);
}
} catch (NamingException e) {
throw new LdapPersistenceException(e);
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.Iterator;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException;
import com.avaje.ebeaninternal.server.type.ScalarType;
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
private final ScalarType<T> collectionScalarType;
public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
super(owner, descriptor, deploy);
this.collectionScalarType = deploy.getCollectionScalarType();
}
public void initialise() {
super.initialise();
}
@Override
public Attribute createAttribute(Object bean) {
Object v = getValue(bean);
if (v == null){
return null;
}
if (ldapAttributeAdapter != null){
return ldapAttributeAdapter.createAttribute(v);
}
BasicAttribute attrs = new BasicAttribute(getDbColumn());
Iterator<?> it = help.getIterator(v);
if (it != null){
while (it.hasNext()) {
Object beanValue = it.next();
Object attrValue = collectionScalarType.toJdbcType(beanValue);
attrs.add(attrValue);
}
}
return attrs;
}
@Override
public void setAttributeValue(Object bean, Attribute attr) {
try {
if (attr != null){
Object beanValue;
if (ldapAttributeAdapter != null){
beanValue = ldapAttributeAdapter.readAttribute(attr);
} else {
boolean vanilla = true;
beanValue = help.createEmpty(vanilla);
BeanCollectionAdd collAdd = help.getBeanCollectionAdd(beanValue, mapKey);
NamingEnumeration<?> en = attr.getAll();
while (en.hasMoreElements()) {
Object attrValue = (Object) en.nextElement();
Object collValue = collectionScalarType.toBeanType(attrValue);
collAdd.addBean(collValue);
}
}
setValue(bean, beanValue);
}
} catch (NamingException e) {
throw new LdapPersistenceException(e);
}
}
}
@@ -1,61 +1,42 @@
/**
* Copyright (C) 2009 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Default implementation for creating BeanControllers.
*/
public class BeanQueryAdapterManager {
private static final Logger logger = Logger.getLogger(BeanQueryAdapterManager.class.getName());
private final List<BeanQueryAdapter> list;
public BeanQueryAdapterManager(BootupClasses bootupClasses){
list = bootupClasses.getBeanQueryAdapters();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addQueryAdapter(DeployBeanDescriptor<?> deployDesc){
for (int i = 0; i < list.size(); i++) {
BeanQueryAdapter c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())){
logger.fine("BeanQueryAdapter on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addQueryAdapter(c);
}
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Default implementation for creating BeanControllers.
*/
public class BeanQueryAdapterManager {
private static final Logger logger = Logger.getLogger(BeanQueryAdapterManager.class.getName());
private final List<BeanQueryAdapter> list;
public BeanQueryAdapterManager(BootupClasses bootupClasses){
list = bootupClasses.getBeanQueryAdapters();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addQueryAdapter(DeployBeanDescriptor<?> deployDesc){
for (int i = 0; i < list.size(); i++) {
BeanQueryAdapter c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())){
logger.fine("BeanQueryAdapter on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addQueryAdapter(c);
}
}
}
}
@@ -1,142 +1,123 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
/**
* Used for associated beans in place of a BeanDescriptor. This is done to avoid
* recursion issues due to the potentially bi-directional and circular
* relationships between beans.
* <p>
* It holds the main deployment information and not all the detail that is held
* in a BeanDescriptor.
* </p>
*/
public class BeanTable {
private static final Logger logger = Logger.getLogger(BeanTable.class.getName());
private final Class<?> beanType;
/**
* The base table.
*/
private final String baseTable;
private final BeanProperty[] idProperties;
/**
* Create the BeanTable.
*/
public BeanTable(DeployBeanTable mutable, BeanDescriptorMap owner) {
this.beanType = mutable.getBeanType();
this.baseTable = InternString.intern(mutable.getBaseTable());
this.idProperties = mutable.createIdProperties(owner);
}
public String toString(){
return baseTable;
}
/**
* Return the base table for this BeanTable.
* This is used to determine the join information
* for associations.
*/
public String getBaseTable() {
return baseTable;
}
/**
* Gets the unqualified base table.
*
* @return the unqualified base table
*/
public String getUnqualifiedBaseTable(){
final String[] chunks = baseTable.split("\\.");
return chunks.length == 2 ? chunks[1] :chunks[0];
}
/**
* Return the Id properties.
*/
public BeanProperty[] getIdProperties() {
return idProperties;
}
/**
* Return the class for this beanTable.
*/
public Class<?> getBeanType() {
return beanType;
}
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse) {
boolean complexKey = false;
BeanProperty[] props = idProperties;
if (idProperties.length == 1){
if (idProperties[0] instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>)idProperties[0];
props = assocOne.getProperties();
complexKey = true;
}
}
for (int i = 0; i < props.length; i++) {
String lc = props[i].getDbColumn();
String fk = lc;
if (foreignKeyPrefix != null){
fk = foreignKeyPrefix+"_"+fk;
}
if (complexKey){
// check to see if we want prefixes by default with complex keys
boolean usePrefixOnComplex = GlobalProperties.getBoolean("ebean.prefixComplexKeys", false);
if (!usePrefixOnComplex){
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
logger.log(Level.FINE, msg);
fk = lc;
}
}
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
if (reverse){
joinCol = joinCol.reverse();
}
join.addJoinColumn(joinCol);
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
/**
* Used for associated beans in place of a BeanDescriptor. This is done to avoid
* recursion issues due to the potentially bi-directional and circular
* relationships between beans.
* <p>
* It holds the main deployment information and not all the detail that is held
* in a BeanDescriptor.
* </p>
*/
public class BeanTable {
private static final Logger logger = Logger.getLogger(BeanTable.class.getName());
private final Class<?> beanType;
/**
* The base table.
*/
private final String baseTable;
private final BeanProperty[] idProperties;
/**
* Create the BeanTable.
*/
public BeanTable(DeployBeanTable mutable, BeanDescriptorMap owner) {
this.beanType = mutable.getBeanType();
this.baseTable = InternString.intern(mutable.getBaseTable());
this.idProperties = mutable.createIdProperties(owner);
}
public String toString(){
return baseTable;
}
/**
* Return the base table for this BeanTable.
* This is used to determine the join information
* for associations.
*/
public String getBaseTable() {
return baseTable;
}
/**
* Gets the unqualified base table.
*
* @return the unqualified base table
*/
public String getUnqualifiedBaseTable(){
final String[] chunks = baseTable.split("\\.");
return chunks.length == 2 ? chunks[1] :chunks[0];
}
/**
* Return the Id properties.
*/
public BeanProperty[] getIdProperties() {
return idProperties;
}
/**
* Return the class for this beanTable.
*/
public Class<?> getBeanType() {
return beanType;
}
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse) {
boolean complexKey = false;
BeanProperty[] props = idProperties;
if (idProperties.length == 1){
if (idProperties[0] instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>)idProperties[0];
props = assocOne.getProperties();
complexKey = true;
}
}
for (int i = 0; i < props.length; i++) {
String lc = props[i].getDbColumn();
String fk = lc;
if (foreignKeyPrefix != null){
fk = foreignKeyPrefix+"_"+fk;
}
if (complexKey){
// check to see if we want prefixes by default with complex keys
boolean usePrefixOnComplex = GlobalProperties.getBoolean("ebean.prefixComplexKeys", false);
if (!usePrefixOnComplex){
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
logger.log(Level.FINE, msg);
fk = lc;
}
}
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
if (reverse){
joinCol = joinCol.reverse();
}
join.addJoinColumn(joinCol);
}
}
}
@@ -1,43 +1,24 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
/**
* Used to convert between collection types.
* <P>
* This typically means wrap and unwrap mutable scala collection types of Buffer, Set and Map.
* </p>
*
* @author rbygrave
*
*/
public interface CollectionTypeConverter {
/**
* Convert the wrapped type to the underlying Java List, Set or Map.
*/
public Object toUnderlying(Object wrapped);
/**
* Wrap the underlying Java List, Set or Map into the final collection type.
*/
public Object toWrapped(Object wrapped);
}
package com.avaje.ebeaninternal.server.deploy;
/**
* Used to convert between collection types.
* <P>
* This typically means wrap and unwrap mutable scala collection types of Buffer, Set and Map.
* </p>
*
* @author rbygrave
*
*/
public interface CollectionTypeConverter {
/**
* Convert the wrapped type to the underlying Java List, Set or Map.
*/
public Object toUnderlying(Object wrapped);
/**
* Wrap the underlying Java List, Set or Map into the final collection type.
*/
public Object toWrapped(Object wrapped);
}
@@ -1,40 +1,21 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
/**
* Holds multiple column unique constraints defined for an entity.
*/
public class CompoundUniqueContraint {
private final String[] columns;
public CompoundUniqueContraint(String[] columns) {
this.columns = columns;
}
/**
* Return the columns that make up this unique constraint.
*/
public String[] getColumns() {
return columns;
}
}
package com.avaje.ebeaninternal.server.deploy;
/**
* Holds multiple column unique constraints defined for an entity.
*/
public class CompoundUniqueContraint {
private final String[] columns;
public CompoundUniqueContraint(String[] columns) {
this.columns = columns;
}
/**
* Return the columns that make up this unique constraint.
*/
public String[] getColumns() {
return columns;
}
}
@@ -1,83 +1,64 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Provides context when performing a bean copy.
*
* @author rbygrave
*/
public class CopyContext {
private final boolean vanillaMode;
private final boolean sharing;
private final PersistenceContext pc;
public CopyContext(boolean vanillaMode, boolean sharing) {
this.vanillaMode = vanillaMode;
this.sharing = sharing;
this.pc = new DefaultPersistenceContext();
}
public CopyContext(boolean vanillaMode) {
this(vanillaMode, false);
}
/**
* Return true if the copy should be a vanilla bean.
*/
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Return true if the copy should be safe for sharing.
*/
public boolean isSharing() {
return sharing;
}
/**
* Return the persistence context used during the copy.
*/
public PersistenceContext getPersistenceContext() {
return pc;
}
/**
* Put the bean if absent into the persistence context.
*/
public Object putIfAbsent(Object id, Object bean){
return pc.putIfAbsent(id, bean);
}
/**
* Return the bean for the given type and id from the persistence context.
*/
public Object get(Class<?> beanType, Object beanId){
return pc.get(beanType, beanId);
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Provides context when performing a bean copy.
*
* @author rbygrave
*/
public class CopyContext {
private final boolean vanillaMode;
private final boolean sharing;
private final PersistenceContext pc;
public CopyContext(boolean vanillaMode, boolean sharing) {
this.vanillaMode = vanillaMode;
this.sharing = sharing;
this.pc = new DefaultPersistenceContext();
}
public CopyContext(boolean vanillaMode) {
this(vanillaMode, false);
}
/**
* Return true if the copy should be a vanilla bean.
*/
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Return true if the copy should be safe for sharing.
*/
public boolean isSharing() {
return sharing;
}
/**
* Return the persistence context used during the copy.
*/
public PersistenceContext getPersistenceContext() {
return pc;
}
/**
* Put the bean if absent into the persistence context.
*/
public Object putIfAbsent(Object id, Object bean){
return pc.putIfAbsent(id, bean);
}
/**
* Return the bean for the given type and id from the persistence context.
*/
public Object get(Class<?> beanType, Object beanId){
return pc.get(beanType, beanId);
}
}
@@ -1,42 +1,39 @@
/**
*
*/
package com.avaje.ebeaninternal.server.deploy;
public class DRawSqlColumnInfo {
final String name;
final String label;
final String propertyName;
final boolean scalarProperty;
public DRawSqlColumnInfo(String name, String label, String propertyName, boolean scalarProperty) {
this.name = name;
this.label = label;
this.propertyName = propertyName;
this.scalarProperty = scalarProperty;
}
public String getName() {
return name;
}
public String getLabel() {
return label;
}
public String getPropertyName() {
return propertyName;
}
public boolean isScalarProperty() {
return scalarProperty;
}
public String toString() {
return "name:" + name + " label:" + label + " prop:" + propertyName;
}
package com.avaje.ebeaninternal.server.deploy;
public class DRawSqlColumnInfo {
final String name;
final String label;
final String propertyName;
final boolean scalarProperty;
public DRawSqlColumnInfo(String name, String label, String propertyName, boolean scalarProperty) {
this.name = name;
this.label = label;
this.propertyName = propertyName;
this.scalarProperty = scalarProperty;
}
public String getName() {
return name;
}
public String getLabel() {
return label;
}
public String getPropertyName() {
return propertyName;
}
public boolean isScalarProperty() {
return scalarProperty;
}
public String toString() {
return "name:" + name + " label:" + label + " prop:" + propertyName;
}
}
@@ -1,80 +1,61 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.HashMap;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.event.BeanFinder;
/**
* Default implementation for BeanFinderFactory.
*/
public class DefaultBeanFinderManager implements BeanFinderManager {
HashMap<Class<?>, BeanFinder<?>> registerFor = new HashMap<Class<?>, BeanFinder<?>>();
public int createBeanFinders(List<Class<?>> finderClassList) {
for (Class<?> cls : finderClassList) {
Class<?> entityType = getEntityClass(cls);
try {
BeanFinder<?> beanFinder = (BeanFinder<?>) cls.newInstance();
registerFor.put(entityType, beanFinder);
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
return registerFor.size();
}
public int getRegisterCount() {
return registerFor.size();
}
/**
* Return the BeanFinder for a given entity type.
*/
@SuppressWarnings("unchecked")
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType) {
return (BeanFinder<T>)registerFor.get(entityType);
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private Class<?> getEntityClass(Class<?> controller){
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanFinder.class);
if (cls == null){
String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.HashMap;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.event.BeanFinder;
/**
* Default implementation for BeanFinderFactory.
*/
public class DefaultBeanFinderManager implements BeanFinderManager {
HashMap<Class<?>, BeanFinder<?>> registerFor = new HashMap<Class<?>, BeanFinder<?>>();
public int createBeanFinders(List<Class<?>> finderClassList) {
for (Class<?> cls : finderClassList) {
Class<?> entityType = getEntityClass(cls);
try {
BeanFinder<?> beanFinder = (BeanFinder<?>) cls.newInstance();
registerFor.put(entityType, beanFinder);
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
return registerFor.size();
}
public int getRegisterCount() {
return registerFor.size();
}
/**
* Return the BeanFinder for a given entity type.
*/
@SuppressWarnings("unchecked")
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType) {
return (BeanFinder<T>)registerFor.get(entityType);
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private Class<?> getEntityClass(Class<?> controller){
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanFinder.class);
if (cls == null){
String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
@@ -1,191 +1,172 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.lib.resource.ResourceContent;
import com.avaje.ebeaninternal.server.lib.resource.ResourceSource;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
/**
* Controls the creation and caching of BeanManager's, BeanDescriptors,
* BeanTable etc for both beans and tables(MapBeans).
* <p>
* Also supports some other deployment features such as type conversion.
* </p>
*/
public class DeployOrmXml {
private static final Logger logger = Logger.getLogger(DeployOrmXml.class.getName());
private final HashMap<String, DNativeQuery> nativeQueryCache;
private final ArrayList<Dnode> ormXmlList;
private final ResourceSource resSource;
public DeployOrmXml(ResourceSource resSource) {
this.resSource = resSource;
this.nativeQueryCache = new HashMap<String, DNativeQuery>();
this.ormXmlList = findAllOrmXml();
initialiseNativeQueries();
}
/**
* Register all the native queries in ALL orm xml deployment.
*/
private void initialiseNativeQueries() {
for (Dnode ormXml : ormXmlList) {
initialiseNativeQueries(ormXml);
}
}
/**
* Register the native queries in this particular orm xml deployment.
*/
private void initialiseNativeQueries(Dnode ormXml) {
Dnode entityMappings = ormXml.find("entity-mappings");
if (entityMappings != null) {
List<Dnode> nq = entityMappings.findAll("named-native-query", 1);
for (int i = 0; i < nq.size(); i++) {
Dnode nqNode = nq.get(i);
Dnode nqQueryNode = nqNode.find("query");
if (nqQueryNode != null) {
String queryContent = nqQueryNode.getNodeContent();
String queryName = (String) nqNode.getAttribute("name");
if (queryName != null && queryContent != null) {
DNativeQuery query = new DNativeQuery(queryContent);
nativeQueryCache.put(queryName, query);
}
}
}
}
}
/**
* Return a native named query.
* <p>
* These are loaded from the orm.xml deployment file.
* </p>
*/
public DNativeQuery getNativeQuery(String name) {
return nativeQueryCache.get(name);
}
private ArrayList<Dnode> findAllOrmXml() {
ArrayList<Dnode> ormXmlList = new ArrayList<Dnode>();
String defaultFile = "orm.xml";
readOrmXml(defaultFile, ormXmlList);
if (!ormXmlList.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (Dnode ox : ormXmlList) {
sb.append(", ").append(ox.getAttribute("ebean.filename"));
}
String loadedFiles = sb.toString().substring(2);
logger.info("Deployment xml [" + loadedFiles + "] loaded.");
}
return ormXmlList;
}
private boolean readOrmXml(String ormXmlName, ArrayList<Dnode> ormXmlList) {
try {
Dnode ormXml = null;
ResourceContent content = resSource.getContent(ormXmlName);
if (content != null) {
// servlet resource or file system...
ormXml = readOrmXml(content.getInputStream());
} else {
// try the classpath...
ormXml = readOrmXmlFromClasspath(ormXmlName);
}
if (ormXml != null) {
ormXml.setAttribute("ebean.filename", ormXmlName);
ormXmlList.add(ormXml);
return true;
} else {
return false;
}
} catch (IOException e) {
logger.log(Level.SEVERE, "error reading orm xml deployment " + ormXmlName, e);
return false;
}
}
private Dnode readOrmXmlFromClasspath(String ormXmlName) throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(ormXmlName);
if (is == null) {
return null;
} else {
return readOrmXml(is);
}
}
private Dnode readOrmXml(InputStream in) throws IOException {
DnodeReader reader = new DnodeReader();
Dnode ormXml = reader.parseXml(in);
in.close();
return ormXml;
}
/**
* Find the deployment xml for a given entity. This will return null if no
* matching deployment xml is found for this entity.
* <p>
* This searches all the ormXml files and returns the first match.
* </p>
*/
public Dnode findEntityDeploymentXml(String className) {
for (Dnode ormXml : ormXmlList) {
Dnode entityMappings = ormXml.find("entity-mappings");
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
if (entities.size() == 1) {
return entities.get(0);
}
}
return null;
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.server.lib.resource.ResourceContent;
import com.avaje.ebeaninternal.server.lib.resource.ResourceSource;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
/**
* Controls the creation and caching of BeanManager's, BeanDescriptors,
* BeanTable etc for both beans and tables(MapBeans).
* <p>
* Also supports some other deployment features such as type conversion.
* </p>
*/
public class DeployOrmXml {
private static final Logger logger = Logger.getLogger(DeployOrmXml.class.getName());
private final HashMap<String, DNativeQuery> nativeQueryCache;
private final ArrayList<Dnode> ormXmlList;
private final ResourceSource resSource;
public DeployOrmXml(ResourceSource resSource) {
this.resSource = resSource;
this.nativeQueryCache = new HashMap<String, DNativeQuery>();
this.ormXmlList = findAllOrmXml();
initialiseNativeQueries();
}
/**
* Register all the native queries in ALL orm xml deployment.
*/
private void initialiseNativeQueries() {
for (Dnode ormXml : ormXmlList) {
initialiseNativeQueries(ormXml);
}
}
/**
* Register the native queries in this particular orm xml deployment.
*/
private void initialiseNativeQueries(Dnode ormXml) {
Dnode entityMappings = ormXml.find("entity-mappings");
if (entityMappings != null) {
List<Dnode> nq = entityMappings.findAll("named-native-query", 1);
for (int i = 0; i < nq.size(); i++) {
Dnode nqNode = nq.get(i);
Dnode nqQueryNode = nqNode.find("query");
if (nqQueryNode != null) {
String queryContent = nqQueryNode.getNodeContent();
String queryName = (String) nqNode.getAttribute("name");
if (queryName != null && queryContent != null) {
DNativeQuery query = new DNativeQuery(queryContent);
nativeQueryCache.put(queryName, query);
}
}
}
}
}
/**
* Return a native named query.
* <p>
* These are loaded from the orm.xml deployment file.
* </p>
*/
public DNativeQuery getNativeQuery(String name) {
return nativeQueryCache.get(name);
}
private ArrayList<Dnode> findAllOrmXml() {
ArrayList<Dnode> ormXmlList = new ArrayList<Dnode>();
String defaultFile = "orm.xml";
readOrmXml(defaultFile, ormXmlList);
if (!ormXmlList.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (Dnode ox : ormXmlList) {
sb.append(", ").append(ox.getAttribute("ebean.filename"));
}
String loadedFiles = sb.toString().substring(2);
logger.info("Deployment xml [" + loadedFiles + "] loaded.");
}
return ormXmlList;
}
private boolean readOrmXml(String ormXmlName, ArrayList<Dnode> ormXmlList) {
try {
Dnode ormXml = null;
ResourceContent content = resSource.getContent(ormXmlName);
if (content != null) {
// servlet resource or file system...
ormXml = readOrmXml(content.getInputStream());
} else {
// try the classpath...
ormXml = readOrmXmlFromClasspath(ormXmlName);
}
if (ormXml != null) {
ormXml.setAttribute("ebean.filename", ormXmlName);
ormXmlList.add(ormXml);
return true;
} else {
return false;
}
} catch (IOException e) {
logger.log(Level.SEVERE, "error reading orm xml deployment " + ormXmlName, e);
return false;
}
}
private Dnode readOrmXmlFromClasspath(String ormXmlName) throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(ormXmlName);
if (is == null) {
return null;
} else {
return readOrmXml(is);
}
}
private Dnode readOrmXml(InputStream in) throws IOException {
DnodeReader reader = new DnodeReader();
Dnode ormXml = reader.parseXml(in);
in.close();
return ormXml;
}
/**
* Find the deployment xml for a given entity. This will return null if no
* matching deployment xml is found for this entity.
* <p>
* This searches all the ormXml files and returns the first match.
* </p>
*/
public Dnode findEntityDeploymentXml(String className) {
for (Dnode ormXml : ormXmlList) {
Dnode entityMappings = ormXml.find("entity-mappings");
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
if (entities.size() == 1) {
return entities.get(0);
}
}
return null;
}
}
@@ -1,71 +1,52 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
/**
* The Exported foreign key and property.
* <p>
* Used to for Assoc Manys to create references etc.
* </p>
*/
public class ExportedProperty {
private final String foreignDbColumn;
private final BeanProperty property;
private final boolean embedded;
public ExportedProperty(boolean embedded, String foreignDbColumn, BeanProperty property) {
this.embedded = embedded;
this.foreignDbColumn = InternString.intern(foreignDbColumn);
this.property = property;
}
/**
* Return true if this is part of an embedded concatinated key.
*/
public boolean isEmbedded() {
return embedded;
}
/**
* Return the property value from the bean.
*/
public Object getValue(Object bean){
return property.getValue(bean);
}
/**
* Return the foreign database column matching this property.
* <p>
* We use this foreign database column in the query predicates
* in preference to a parentProperty.idProperty = value.
* Just using the foreign database column avoids triggering
* a join to the 'parent' table.
* </p>
*/
public String getForeignDbColumn() {
return foreignDbColumn;
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.core.InternString;
/**
* The Exported foreign key and property.
* <p>
* Used to for Assoc Manys to create references etc.
* </p>
*/
public class ExportedProperty {
private final String foreignDbColumn;
private final BeanProperty property;
private final boolean embedded;
public ExportedProperty(boolean embedded, String foreignDbColumn, BeanProperty property) {
this.embedded = embedded;
this.foreignDbColumn = InternString.intern(foreignDbColumn);
this.property = property;
}
/**
* Return true if this is part of an embedded concatinated key.
*/
public boolean isEmbedded() {
return embedded;
}
/**
* Return the property value from the bean.
*/
public Object getValue(Object bean){
return property.getValue(bean);
}
/**
* Return the foreign database column matching this property.
* <p>
* We use this foreign database column in the query predicates
* in preference to a parentProperty.idProperty = value.
* Just using the foreign database column avoids triggering
* a join to the 'parent' table.
* </p>
*/
public String getForeignDbColumn() {
return foreignDbColumn;
}
}
@@ -1,371 +1,352 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Represents a node in the Inheritance tree. Holds information regarding Super
* Subclass support.
*/
public class InheritInfo {
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorColumn;
private final int discriminatorType;
private final int discriminatorLength;
private final String where;
private final Class<?> type;
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
private final InheritInfo parent;
private final InheritInfo root;
private BeanDescriptor<?> descriptor;
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a save cascade on it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()){
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()){
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a delete cascade on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null){
return prop;
}
}
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
}
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
if (discValue == null) {
return null;
}
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
String m = "Inheritance type for discriminator value [" + discValue + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
String m = "Inheritance type for bean type [" + beanType.getName() + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
}
/**
* Create an EntityBean for this type.
*/
public Object createBean(boolean vanillaMode) {
return descriptor.createBean(vanillaMode);
}
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
String clsName = SubClassUtil.getSuperClassName(beanType.getName());
return typeMap.get(clsName);
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
String clsName = SubClassUtil.getSuperClassName(info.getType().getName());
typeMap.put(clsName, info);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
return where;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Represents a node in the Inheritance tree. Holds information regarding Super
* Subclass support.
*/
public class InheritInfo {
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorColumn;
private final int discriminatorType;
private final int discriminatorLength;
private final String where;
private final Class<?> type;
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
private final InheritInfo parent;
private final InheritInfo root;
private BeanDescriptor<?> descriptor;
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a save cascade on it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()){
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()){
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a delete cascade on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null){
return prop;
}
}
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
}
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
if (discValue == null) {
return null;
}
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
String m = "Inheritance type for discriminator value [" + discValue + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
String m = "Inheritance type for bean type [" + beanType.getName() + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
}
/**
* Create an EntityBean for this type.
*/
public Object createBean(boolean vanillaMode) {
return descriptor.createBean(vanillaMode);
}
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
String clsName = SubClassUtil.getSuperClassName(beanType.getName());
return typeMap.get(clsName);
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
String clsName = SubClassUtil.getSuperClassName(info.getType().getName());
typeMap.put(clsName, info);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
return where;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
}
@@ -1,87 +1,68 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represents the type of a OneToMany or ManyToMany property.
*/
public class ManyType {
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
public enum Underlying {
LIST,
SET,
MAP
}
private final SpiQuery.Type queryType;
private final Underlying underlying;
private final CollectionTypeConverter typeConverter;
private ManyType(Underlying underlying) {
this(underlying, null);
}
public ManyType(Underlying underlying, CollectionTypeConverter typeConverter) {
this.underlying = underlying;
this.typeConverter = typeConverter;
switch (underlying) {
case LIST:
queryType = SpiQuery.Type.LIST;
break;
case SET:
queryType = SpiQuery.Type.SET;
break;
default:
queryType = SpiQuery.Type.MAP;
break;
}
}
/**
* Return the matching Query type.
*/
public SpiQuery.Type getQueryType() {
return queryType;
}
/**
* Return the underlying type.
*/
public Underlying getUnderlying() {
return underlying;
}
/**
* Return the type converter if there is one.
*/
public CollectionTypeConverter getTypeConverter() {
return typeConverter;
}
}
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represents the type of a OneToMany or ManyToMany property.
*/
public class ManyType {
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
public enum Underlying {
LIST,
SET,
MAP
}
private final SpiQuery.Type queryType;
private final Underlying underlying;
private final CollectionTypeConverter typeConverter;
private ManyType(Underlying underlying) {
this(underlying, null);
}
public ManyType(Underlying underlying, CollectionTypeConverter typeConverter) {
this.underlying = underlying;
this.typeConverter = typeConverter;
switch (underlying) {
case LIST:
queryType = SpiQuery.Type.LIST;
break;
case SET:
queryType = SpiQuery.Type.SET;
break;
default:
queryType = SpiQuery.Type.MAP;
break;
}
}
/**
* Return the matching Query type.
*/
public SpiQuery.Type getQueryType() {
return queryType;
}
/**
* Return the underlying type.
*/
public Underlying getUnderlying() {
return underlying;
}
/**
* Return the type converter if there is one.
*/
public CollectionTypeConverter getTypeConverter() {
return typeConverter;
}
}
@@ -1,61 +1,42 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Default implementation for creating BeanControllers.
*/
public class PersistControllerManager {
private static final Logger logger = Logger.getLogger(PersistControllerManager.class.getName());
private final List<BeanPersistController> list;
public PersistControllerManager(BootupClasses bootupClasses){
list = bootupClasses.getBeanPersistControllers();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addPersistControllers(DeployBeanDescriptor<?> deployDesc){
for (int i = 0; i < list.size(); i++) {
BeanPersistController c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())){
logger.fine("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistController(c);
}
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Default implementation for creating BeanControllers.
*/
public class PersistControllerManager {
private static final Logger logger = Logger.getLogger(PersistControllerManager.class.getName());
private final List<BeanPersistController> list;
public PersistControllerManager(BootupClasses bootupClasses){
list = bootupClasses.getBeanPersistControllers();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addPersistControllers(DeployBeanDescriptor<?> deployDesc){
for (int i = 0; i < list.size(); i++) {
BeanPersistController c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())){
logger.fine("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistController(c);
}
}
}
}
@@ -1,85 +1,66 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Manages the assignment/registration of BeanPersistListener with their
* respective DeployBeanDescriptor's.
*/
public class PersistListenerManager {
private static final Logger logger = Logger.getLogger(PersistListenerManager.class.getName());
private final List<BeanPersistListener<?>> list;
public PersistListenerManager(BootupClasses bootupClasses) {
list = bootupClasses.getBeanPersistListeners();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
@SuppressWarnings("unchecked")
public <T> void addPersistListeners(DeployBeanDescriptor<T> deployDesc) {
for (int i = 0; i < list.size(); i++) {
BeanPersistListener<?> c = list.get(i);
if (isRegisterFor(deployDesc.getBeanType(), c)) {
logger.fine("BeanPersistListener on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistListener((BeanPersistListener<T>) c);
}
}
}
public static boolean isRegisterFor(Class<?> beanType, BeanPersistListener<?> c) {
Class<?> listenerEntity = getEntityClass(c.getClass());
return beanType.equals(listenerEntity);
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private static Class<?> getEntityClass(Class<?> controller) {
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanPersistListener.class);
if (cls == null) {
String msg = "Could not determine the entity class (generics parameter type) from " + controller
+ " using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Manages the assignment/registration of BeanPersistListener with their
* respective DeployBeanDescriptor's.
*/
public class PersistListenerManager {
private static final Logger logger = Logger.getLogger(PersistListenerManager.class.getName());
private final List<BeanPersistListener<?>> list;
public PersistListenerManager(BootupClasses bootupClasses) {
list = bootupClasses.getBeanPersistListeners();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
@SuppressWarnings("unchecked")
public <T> void addPersistListeners(DeployBeanDescriptor<T> deployDesc) {
for (int i = 0; i < list.size(); i++) {
BeanPersistListener<?> c = list.get(i);
if (isRegisterFor(deployDesc.getBeanType(), c)) {
logger.fine("BeanPersistListener on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistListener((BeanPersistListener<T>) c);
}
}
}
public static boolean isRegisterFor(Class<?> beanType, BeanPersistListener<?> c) {
Class<?> listenerEntity = getEntityClass(c.getClass());
return beanType.equals(listenerEntity);
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private static Class<?> getEntityClass(Class<?> controller) {
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanPersistListener.class);
if (cls == null) {
String msg = "Could not determine the entity class (generics parameter type) from " + controller
+ " using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
@@ -1,104 +1,85 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
/**
* For abstract classes that hold the id property we need to
* use reflection to get the id values some times.
* <p>
* This provides the BeanReflectGetter objects to do that.
* </p>
* @author rbygrave
*/
public class ReflectGetter {
/**
* Create a reflection based BeanReflectGetter for getting the
* id from abstract inheritance hierarchy object.
*/
public static BeanReflectGetter create(DeployBeanProperty prop) {
if (!prop.isId()){
// not expecting this to ever be used/called
return new NonIdGetter(prop.getFullBeanName());
} else {
String property = prop.getFullBeanName();
Method readMethod = prop.getReadMethod();
if (readMethod == null){
String m = "Abstract class with no readMethod for "+property;
throw new RuntimeException(m);
}
return new IdGetter(property, readMethod);
}
}
public static class IdGetter implements BeanReflectGetter {
public static final Object[] NO_ARGS = new Object[0];
private final Method readMethod;
private final String property;
public IdGetter(String property, Method readMethod) {
this.property = property;
this.readMethod = readMethod;
}
public Object get(Object bean) {
try {
return readMethod.invoke(bean, NO_ARGS);
} catch (Exception e) {
String m = "Error on ["+property+"] using readMethod "+readMethod;
throw new RuntimeException(m, e);
}
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
public static class NonIdGetter implements BeanReflectGetter {
private final String property;
public NonIdGetter(String property) {
this.property = property;
}
public Object get(Object bean) {
String m = "Not expecting this method to be called on ["+property
+"] as it is a NON ID property on an abstract class";
throw new RuntimeException(m);
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
}
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
/**
* For abstract classes that hold the id property we need to
* use reflection to get the id values some times.
* <p>
* This provides the BeanReflectGetter objects to do that.
* </p>
* @author rbygrave
*/
public class ReflectGetter {
/**
* Create a reflection based BeanReflectGetter for getting the
* id from abstract inheritance hierarchy object.
*/
public static BeanReflectGetter create(DeployBeanProperty prop) {
if (!prop.isId()){
// not expecting this to ever be used/called
return new NonIdGetter(prop.getFullBeanName());
} else {
String property = prop.getFullBeanName();
Method readMethod = prop.getReadMethod();
if (readMethod == null){
String m = "Abstract class with no readMethod for "+property;
throw new RuntimeException(m);
}
return new IdGetter(property, readMethod);
}
}
public static class IdGetter implements BeanReflectGetter {
public static final Object[] NO_ARGS = new Object[0];
private final Method readMethod;
private final String property;
public IdGetter(String property, Method readMethod) {
this.property = property;
this.readMethod = readMethod;
}
public Object get(Object bean) {
try {
return readMethod.invoke(bean, NO_ARGS);
} catch (Exception e) {
String m = "Error on ["+property+"] using readMethod "+readMethod;
throw new RuntimeException(m, e);
}
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
public static class NonIdGetter implements BeanReflectGetter {
private final String property;
public NonIdGetter(String property) {
this.property = property;
}
public Object get(Object bean) {
String m = "Not expecting this method to be called on ["+property
+"] as it is a NON ID property on an abstract class";
throw new RuntimeException(m);
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
}

Some files were not shown because too many files have changed in this diff Show More